PHP SQLite - 准备好的语句行为异常?
PHP SQLite - prepared statement misbehaves?
我有以下 SQLite table
CREATE TABLE keywords
(
id INTEGER PRIMARY KEY,
lang INTEGER NOT NULL,
kwd TEXT NOT NULL,
count INTEGER NOT NULL DEFAULT 0,
locs TEXT NOT NULL DEFAULT '{}'
);
CREATE UNIQUE INDEX kwd ON keywords(lang,kwd);
在 PHP 中工作 我通常需要在此 table 中插入关键字,或者如果关键字已存在则更新行数。举个例子
$langs = array(0,1,2,3,4,5);
$kwds = array('noel,canard,foie gras','','','','','');
我现在这些数据运行通过下面的代码
$len = count($langs);
$klen = count($kwds);
$klen = ($klen < $len)?$klen:$len;
$sqlite = new SQLite3('/path/to/keywords.sqlite');
$iStmt = $sqlite->prepare("INSERT OR IGNORE INTO keywords (lang,kwd)
VALUES(:lang,:kwd)");
$sStmt = $sqlite->prepare("SELECT rowid FROM keywords WHERE lang = :lang
AND kwd = :kwd");
if (!$iStmt || !$sStmt) return;
for($i=0;$i < $klen;$i++)
{
$keywords = $kwds[$i];
if (0 === strlen($keywords)) continue;
$lang = intval($langs[$i]);
$keywords = explode(',',$keywords);
for($j=0;$j < count($keywords);$j++)
{
$keyword = $keywords[$j];
if (0 === strlen($keyword)) continue;
$iStmt->bindValue(':kwd',$keyword,SQLITE3_TEXT);
$iStmt->bindValue(':lang',$lang,SQLITE3_INTEGER);
$sStmt->bindValue(':lang',$lang,SQLITE3_INTEGER);
$sStmt->bindValue(':kwd',$keyword,SQLITE3_TEXT);
trigger_error($keyword);
$iStmt->execute();
$sqlite->exec("UPDATE keywords SET count = count + 1 WHERE lang =
'{$lang}' AND kwd = '{$keyword}';");
$rslt = $sStmt->execute();
trigger_error($sqlite->lastErrorMsg());
trigger_error(json_encode($rslt->fetchArray()));
}
}
生成以下 trigger_error
输出
Keyword: noel
Last error: not an error
SELECT Result: {"0":1,"id":1}
Keyword: canard
Last Error: not an error
SELECT Reult:false
Keyword:foiegras
Last Error: not an error
SELECT Result: false
从 SQLite 命令行中,我看到 table 中存在三个行条目并且正确,id/rowid 列分别设置为 1、2 和 3。 lastErrorMsg
不报告错误,但三个 $rslt->fetchArray()
语句中的两个返回 false
而不是具有 rowid/id
属性的数组。那么我在这里做错了什么?
我对此进行了更多调查,发现了根本原因。在我的原始代码中,第一个 SQLite3::execute
- $iStmt-execute()
- 的结果没有被分配给任何东西。我没有看到获取和解释该结果的任何特殊原因。当我将该行代码更改为读取 $rslt = $iStmt->execute()
时,我得到了预期的结果 - 正确报告了插入的三行中的 rowid/id
。
就好像 PHP SQLite3 扩展在内部缓冲了 SQLiteStatement::execute
函数调用的结果。当我在 运行ning 这样的语句中跳过作业时,$sStmt->execute()
实际上是在获取先前的结果。这是我在不知道 PHP SQLite3 扩展的内部工作原理的情况下的解释。也许更了解扩展的人想发表评论。
在 trigger_error(json_encode($rslt->fetchArray()));
之后添加 $rslt = NONE;
并显示正确的结果。
FetchArray 只能调用一次,并且 php 无法检测到变量已更改。我还尝试将 bindValue
更改为 bindParam
并将其移动到循环之前,但这与主要问题无关。
我认为除非 php 中存在错误,否则我的解决方案应该不会起作用。我对这门语言太陌生了,无法对这种观点充满信心,并希望帮助验证它。 好吧,这不是错误,而是违反了最小意外原则。该对象仍然存在于内存中,因此没有完成它或重置变量,就不会触发获取数组。
我有以下 SQLite table
CREATE TABLE keywords
(
id INTEGER PRIMARY KEY,
lang INTEGER NOT NULL,
kwd TEXT NOT NULL,
count INTEGER NOT NULL DEFAULT 0,
locs TEXT NOT NULL DEFAULT '{}'
);
CREATE UNIQUE INDEX kwd ON keywords(lang,kwd);
在 PHP 中工作 我通常需要在此 table 中插入关键字,或者如果关键字已存在则更新行数。举个例子
$langs = array(0,1,2,3,4,5);
$kwds = array('noel,canard,foie gras','','','','','');
我现在这些数据运行通过下面的代码
$len = count($langs);
$klen = count($kwds);
$klen = ($klen < $len)?$klen:$len;
$sqlite = new SQLite3('/path/to/keywords.sqlite');
$iStmt = $sqlite->prepare("INSERT OR IGNORE INTO keywords (lang,kwd)
VALUES(:lang,:kwd)");
$sStmt = $sqlite->prepare("SELECT rowid FROM keywords WHERE lang = :lang
AND kwd = :kwd");
if (!$iStmt || !$sStmt) return;
for($i=0;$i < $klen;$i++)
{
$keywords = $kwds[$i];
if (0 === strlen($keywords)) continue;
$lang = intval($langs[$i]);
$keywords = explode(',',$keywords);
for($j=0;$j < count($keywords);$j++)
{
$keyword = $keywords[$j];
if (0 === strlen($keyword)) continue;
$iStmt->bindValue(':kwd',$keyword,SQLITE3_TEXT);
$iStmt->bindValue(':lang',$lang,SQLITE3_INTEGER);
$sStmt->bindValue(':lang',$lang,SQLITE3_INTEGER);
$sStmt->bindValue(':kwd',$keyword,SQLITE3_TEXT);
trigger_error($keyword);
$iStmt->execute();
$sqlite->exec("UPDATE keywords SET count = count + 1 WHERE lang =
'{$lang}' AND kwd = '{$keyword}';");
$rslt = $sStmt->execute();
trigger_error($sqlite->lastErrorMsg());
trigger_error(json_encode($rslt->fetchArray()));
}
}
生成以下 trigger_error
输出
Keyword: noel Last error: not an error SELECT Result: {"0":1,"id":1}
Keyword: canard Last Error: not an error
SELECT Reult:falseKeyword:foiegras Last Error: not an error SELECT Result: false
从 SQLite 命令行中,我看到 table 中存在三个行条目并且正确,id/rowid 列分别设置为 1、2 和 3。 lastErrorMsg
不报告错误,但三个 $rslt->fetchArray()
语句中的两个返回 false
而不是具有 rowid/id
属性的数组。那么我在这里做错了什么?
我对此进行了更多调查,发现了根本原因。在我的原始代码中,第一个 SQLite3::execute
- $iStmt-execute()
- 的结果没有被分配给任何东西。我没有看到获取和解释该结果的任何特殊原因。当我将该行代码更改为读取 $rslt = $iStmt->execute()
时,我得到了预期的结果 - 正确报告了插入的三行中的 rowid/id
。
就好像 PHP SQLite3 扩展在内部缓冲了 SQLiteStatement::execute
函数调用的结果。当我在 运行ning 这样的语句中跳过作业时,$sStmt->execute()
实际上是在获取先前的结果。这是我在不知道 PHP SQLite3 扩展的内部工作原理的情况下的解释。也许更了解扩展的人想发表评论。
在 trigger_error(json_encode($rslt->fetchArray()));
之后添加 $rslt = NONE;
并显示正确的结果。
FetchArray 只能调用一次,并且 php 无法检测到变量已更改。我还尝试将 bindValue
更改为 bindParam
并将其移动到循环之前,但这与主要问题无关。
我认为除非 php 中存在错误,否则我的解决方案应该不会起作用。我对这门语言太陌生了,无法对这种观点充满信心,并希望帮助验证它。 好吧,这不是错误,而是违反了最小意外原则。该对象仍然存在于内存中,因此没有完成它或重置变量,就不会触发获取数组。