9

この質問が何度も寄せられていることは知っていますが、多くの質問への回答を読んでも、このエラーが表示される理由を理解できません。

致命的なエラー: キャッチされない例外 'PDOException' とメッセージ 'SQLSTATE[HY000]: 一般エラー: 2014 他のバッファリングされていないクエリがアクティブな間はクエリを実行できません。PDOStatement::fetchAll() の使用を検討してください。または、コードが mysql に対してのみ実行される場合は、PDO::MYSQL_ATTR_USE_BUFFERED_QUERY 属性を設定して、クエリのバッファリングを有効にすることができます。

最初に奇妙な点は、ローカルホスト (wampserver) ではエラーが発生しないのに、Web サーバーではエラーが発生することです。私のローカルホストの PHP バージョンは 5.3.10 で、Web サーバーのバージョンは 5.3.13 です。

このエラーの原因は、前のクエリからバッファにデータが残っているときにクエリを作成していることです。これは私には当てはまりません。すべてのデータをエコーアウトしましたが、クエリで返されたすべての行がフェッチされていることを知っています。

そうは言っても、クエリの 1 つをfetchAll代わりに変更fetchすることで問題が解決することがわかりましたが、返されたすべての行が読み取られていることがわかっているため、問題は解決しません。クエリに使用fetchAllしたとき(ループで作成されている)、ループごとに配列を出力しましたが、ループ内の各クエリの配列には1つのアイテムしかありませんでした。

もう一つ情報です。PDOエラーをスローするのは、私が変更したfetchAll(エラーが消える)クエリではありません.phpファイルの後半に、エラーをスローする別のクエリがあります。私のファイルは基本的に次のようなものです:

... code ...

query 1

... code ...

loop
query 2
end loop

... code ... 

query 3

クエリ 3 をコメントアウトすると、エラーは発生しません。クエリ 2をコメント アウトするか、に変更するとfetchAll、エラーは発生しません。クエリ 1 は何の影響もありません。

また、ページ上のすべてのクエリに(同時に)追加しようとしたことも付け加えLIMIT 1ておきますが、エラーはまだ残っています。これは、バッファに未読データがないことを証明していると思いますよね?

本当に困っていますので、アドバイスよろしくお願いします。誰かが尋ねる前に、このための完全なコードを投稿することはできませんが、ここに私のコードの簡略化されたバージョンがあります:

$stmt = $this->db->prepare('SELECT ... :par LIMIT 1');
makeQuery($stmt, array(':par' => $var));
$row = $stmt->fetch(PDO::FETCH_ASSOC);


$stmt = $this->db->prepare('SELECT ... :par LIMIT 1');

for loop
    makeQuery($stmt, array(':par' => $var));
    $row2 = $stmt->fetch(PDO::FETCH_ASSOC);
    ... [use row2] ...
end for loop


$stmt = $this->db->prepare('SELECT ... :par LIMIT 1');
makeQuery($stmt, array(':par' => $var));
$row3 = $stmt->fetch(PDO::FETCH_ASSOC);

ここにありmakeQuery()ます。

/**************************************************************************************************************
* Function: makeQuery                                                                                         *
* Desc: Makes a PDO query.                                                                                    *
* Pre conditions: The statement/query and an array of named parameters (may be empty) must be passed.         *
* Post conditions: The PDO query is executed. Exceptions are caught, displayed, and page execution stopped.   *
**************************************************************************************************************/
function makeQuery($stmt, $array, $errMsg = '')
{
    try 
    {
        $stmt->execute($array);
    }
    catch (PDOException $e) 
    {
        print $errMsg != ''?$errMsg:"Error!: " . $e->getMessage() . "<br/>";
        die();
    }
}

ご協力いただきありがとうございます!

編集:クエリ2の後に次のことも試しました(それが問題の原因であるように思われるため:

$row2 = $stmt->fetch(PDO::FETCH_ASSOC); var_dump($row2);

出力は次のとおりです。

bool(false) 

PDO のバグに遭遇しましたか?

4

3 に答える 3

4

行フェッチの試行が失敗するまでフェッチする必要があります。結果セットに 1 行しかない場合があり、1 回のフェッチで十分だと思いますが、そうではありません (バッファリングされていないクエリを使用している場合)。PDO は、次の行をフェッチしようとする最後に到達するまで、何行あるのかわかりませんが、失敗します。

おそらく、完全に「フェッチが失敗するまでフェッチ」しなかったステートメントが他にもあるでしょう。はい、ステートメントの1 つでフェッチが失敗するまでフェッチしているように見えますが、それはすべてのステートメントで失敗したという意味ではありません。

明確にするために-execute()を介してクエリを実行すると、データベースからphpにフェッチする必要がある結果セットが作成されます。PDO は、一度に (接続ごとに) これらの「取得中の結果セット」を 1 つしか処理できません。execute() への別の呼び出しから別の結果セットのフェッチを開始する前に、結果セットを最後まで完全にフェッチする必要があります。

「fetch() が失敗するまで fetch() を呼び出す」と、結果がなくなるために fetch() への最後の呼び出しが失敗したときに、結果の最後に達したという事実が PDO によって内部的に記録されます。PDO は、結果が完全にフェッチされたことを確認し、その結果セットに対して確立された php と db の間の内部リソースをクリーンアップして、他のクエリを作成/フェッチできるようにします。

PDO を「fetch() が失敗するまで fetch() を呼び出す」ようにする方法は他にもあります。

  1. すべての行を単純にフェッチする fetchAll() を使用するだけで、結果セットの最後にヒットします。
  2. または単に closeCursor() を呼び出します

*closeCursor() のソースを見ると、デフォルトの実装は文字どおり行をフェッチし、最後に到達するまでそれらを破棄します。明らかにcで書かれていますが、多かれ少なかれこれを行います:

function closeCursor() {
    while ($row = $stmt->fetch()) {}
    $this->stmtFullyFetched = true;
}

一部の db ドライバーは、誰も気にしない大量の行をフェッチする必要がない、より効率的な実装を備えている場合がありますが、それが PDO のデフォルトの方法です。ともかく...

通常、バッファリングされたクエリを使用する場合、これらの問題は発生しません。その理由は、バッファリングされたクエリを使用すると、それらを実行した直後に、PDO が自動的に db の結果を php メモリに完全にフェッチするため、「fetch() が失敗するまで fetch() を呼び出す」部分を自動的に実行するためです。後で fetch() または fetchAll() を自分で呼び出すと、db からではなく、php メモリから結果がフェッチされます。したがって、基本的に、バッファリングされたクエリを使用すると、結果セットはすぐに完全にフェッチされるため、同時に複数の「フェッチ中の結果セット」を持つ機会はありません (php はシングルスレッドであるため、2 つのクエリの可能性はありません)。同時に実行されます)。

これを考えると:

$sql = "select * from test.a limit 1";
$stmt = $dbh->prepare($sql);
$stmt->execute(array());

結果セットを完全に取得する方法 (最初の行のみが必要であると仮定):

$row = $stmt->fetch();
$stmt->closeCursor();

また

list($row) = $stmt->fetchAll(); //tricky

また

$row = $stmt->fetch();
while ($stmt->fetch()) {}
于 2012-07-15T17:48:47.547 に答える
2

この問題に何日も苦労した後、最終的にこれがうまくいくことがわかりました:

$db = new PDO ($cnstring, $user, $pwd);
$db->setAttribute (PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
于 2013-09-24T15:25:01.307 に答える