目前 SQLite3 PHP 繫結(不是 SQLite3 本身)中存在一個問題,導致所有查詢都會執行兩次。顯然這個問題已經存在很長一段時間了。
更多資訊請參見:https://bugs.php.net/bug.php?id=64531
在發現上述問題之前,我發布了這個:http://stackoverflow.com/questions/36617708/odd-behavior-with-sqlite3-non-select-prepared-transactions-and-fetcharray(包含可複製貼上的錯誤示範)
解決方法:我強烈建議將任何可能在非 SELECT 查詢結果上運行 fetchArray() 的程式碼包裝在 numColumns() 檢查中,如下所示
<?php
$op = $db->prepare(...);
$r = $op->execute(); // 查詢 #1
if ($r->numColumns()) { // 返回欄位數,這裡用作真/假測試
while ($row = $r->fetchArray(SQLITE3_ASSOC)) { // 查詢 #2
// 您的程式碼寫在這裡
}
}
?>
說明
- 查詢 #1 是 SQLite3 查詢第一次執行的地方,查詢 #2 是查詢再次執行的地方。是的,*所有*內容都會執行兩次;這就是錯誤所在。
- 如果您的程式碼只會讀取而不會修改資料庫(例如,不會導致修改資料庫的觸發器執行的 SELECT),那就沒問題。您的查詢會執行兩次,但不會改變結果。
- 如果您的程式碼會寫入資料庫 - 例如 INSERT - 如果欄位數為零,則**不得**執行 fetchArray()(並再次執行查詢)。
- 手冊中沒有記載,但在這裡 -https://php.dev.org.tw/manual/en/book.sqlite3.php#113144 - 用戶 'bohwaz' 提到自 PHP 5.3.11 起還有一個 SQLite3Stmt::readOnly() 函式,它會告訴您是否剛寫入資料庫。這目前沒有文件說明,但可能是比 numColumns() 更合適的替代方案(我不確定它的作用,它可能是一樣的)。
對於使用 SQLite3 的高負載工作,您可能會更喜歡 PDO。諷刺的是,這個綁定更輕量,並提供對一些 SQLite3 特定基元和行為的直接訪問……但它會執行所有查詢兩次。
[[給版主的說明(本節在閱讀後可以刪除;我也樂於接受以下內容的意見回饋)
- 請不要將此評論視為錯誤報告 - 我只是希望其他人也能意識到這個問題,這樣他們就不必花費數小時苦思冥想。 :P
- 截至此評論提交之日,此頁面有一個未批准的差異卡在 DocBook 中,因此我無法添加類似「由於錯誤 #64531,建議您將 fetchArray() 包裹在 numColumns() 中……」之類的內容,我認為在修復此錯誤之前,這比此評論更有份量。]]