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.net/manual/en/book.sqlite3.php#113144 - 用户'bohwaz'提到自PHP 5.3.11以来还有一个SQLite3Stmt::readOnly()函数,它会告诉你是否刚刚写入数据库。目前这没有记录,但可能是numColumns()的更合适的替代方案(我不确定它做什么,它可能和numColumns()一样)。
对于使用SQLite3的大量工作,你可能更喜欢PDO。讽刺的是,此绑定更轻量级,并提供对某些SQLite3特定原语和行为的直接访问……但它会将所有查询都执行两次。
[[给版主的说明(此部分可在阅读后删除;我也欢迎对以下内容的反馈)
- 请不要将此评论视为错误报告——我只是想让其他人意识到这个问题,这样他们就不必花几个小时抓耳挠腮了。:P
- 在提交此评论之日,此页面有一个未批准的diff卡在DocBook中,所以我无法添加类似“由于错误#64531,建议你将fetchArray()包装在numColumns()中……”的内容,我认为这比这个评论更有分量,直到这个错误被修复。]]