PDO 准备语句 "Invalid Parameter Number"
PDO Prepared Statement "Invalid Parameter Number"
我正在使用一个函数,该函数一直在我的代码中运行,现在我正在慢慢地从 MySQLi 转移到 PDO - 这个函数应该 "simply" 采用 SQL 语句和一个变量数组并设置准备好的语句,执行它和 returns 一个带有 "success/fail" 代码的数组,然后是 lastInsertId。
该函数到目前为止一直运行良好,考虑到我遇到的错误(而且我是 PDO 的新手)我想知道问题出在哪里。
首先,函数本身非常良性(常量在别处定义)
function dbConnect(){
try {
if(!defined('PDO::ATTR_DRIVER_NAME')){
debugPrint('Error: PDO unavailable');
return false;
}
$dsn = 'mysql:host=' . DB_SERVER . ';dbname=' . DB_NAME . ';charset=UTF8';
$opt = [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
];
$db = new PDO($dsn, DB_USER, DB_PASS, $opt);
return $db;
} catch (PDOException $e) {
debugPrint($e->getMessage());
return false;
}
}
function dbInsert($sql, $param){
$ret = [];
if(!$pdo = dbConnect()){
$ret[] = 1;
return $ret;
}
if(!$stmt = $pdo->prepare($sql){
echo $pdo->errorInfo();
$ret[] = 1;
return $ret;
}
if(!$stmt->execute($param)){
echo $pdo->errorInfo();
$ret[] = 1;
return $ret;
} else {
$ret[] = 0;
$ret[1] = $pdo->lastInsertId();
return $ret;
}
我正在编写的代码只是检查电子邮件地址是否出现在 table 中,如果没有出现,则将其插入。我在我的应用程序中做了很多检查,大约 50% 的时间提供的电子邮件将是现有条目。
$sql = 'insert into customer (email) select :email from DUAL where not exists (select 1 from customer where email = :email)';
$bind = [':email' => 'user@bogus.org'];
这是迄今为止我见过的最简洁的方法,它是对数据库的一次调用并使用准备好的语句。但是,我收到 PDO 错误 "Invalid parameter number" 和堆栈跟踪错误。我已经打开 MySQL 日志记录,看来准备工作正常...
Prepare insert into customer (email) select ? from DUAL where not exists (select 1 from customer where email = ?)
...所以我只能认为这是绑定,考虑到参数错误的数量,这很有意义,但异常似乎来自执行:
PHP Fatal error: Uncaught PDOException: SQLSTATE[HY093]: Invalid parameter number in {filename}:78
Stack trace:
#0 {filename}:78: PDOStatement->execute(Array)
#1 {callingFile(33)}: dbInsert('insert into cus...', Array)
#2 {main}
thrown in {filename} on line 78
由于我是 PDO 的新手并且渴望学习,我想知道我应该在哪里寻找解决这个问题的方法。
我喜欢 PDO 的功能,我希望这不是一个需要解决的大问题,但目前我不知道为什么会出现此错误抛出。我确实想知道是不是因为我用的是“1”
注意:1 我知道我可以使电子邮件在 table 中唯一,但这意味着 $pdo->execute returns为非零,使我的函数 return 成为非零(错误)(至少在我看来)并非严格如此。
注意:2 我也知道我可以先检查 table 以查看电子邮件是否存在,如果不存在则插入:但是,我不确定这是最好的做事方式(并且由于我正在更新的代码目前正在这样做,我认为让它成为 PDO'ed 以及可能对 SQL 调用更聪明他们自己)。
注意:3 我也将 SQL 语句更改为以下内容:
$sql = 'insert ignore into customer (email) values (:email)';
$bind = [':email' => 'user@bogus.org'];
这是 "OK",但是如果存在现有电子邮件,则收件人 table 中的 auto_incremented 主键将跳过(再次尝试保持一切干净、简单和合乎逻辑的),我真的不想通过附加一个(慢速)语句来改变 table 以将 auto_increment 设置为 1
来解决这个问题
1) 在你的函数 dbInsert
中,你错过了 if
语句的最后一个括号:
if (!$stmt = $pdo->prepare($sql)) {...}
2) 如 PDO::prepare 的官方文档所述:
You cannot use a named parameter marker of the same name more than
once in a prepared statement, unless emulation mode is on.
例如不允许在 sql 语句中多次使用 :email
,除非您激活 ATTR_EMULATE_PREPARES
选项:
PDO::ATTR_EMULATE_PREPARES => TRUE
如果不这样做,您会收到与您所提供的错误完全相同的信息。
因此,要么应用上面的建议,要么让仿真保持不变(例如 FALSE
)并使用两个不同的命名参数标记(:email1
和 :email2
):
$email = 'user@bogus.org';
$sql = 'insert into customer (email) select :email1 from DUAL where not exists (select 1 from customer where email = :email2)';
$bind = [
':email1' => $email,
':email2' => $email,
];
或者您可以使用问号 (?) 参数标记:
$email = 'user@bogus.org';
$sql = 'insert into customer (email) select ? from DUAL where not exists (select 1 from customer where email = ?)';
$bind = [
1 => $email,
2 => $email,
];
作为非常好的资源,我向您推荐 this 教程。
我正在使用一个函数,该函数一直在我的代码中运行,现在我正在慢慢地从 MySQLi 转移到 PDO - 这个函数应该 "simply" 采用 SQL 语句和一个变量数组并设置准备好的语句,执行它和 returns 一个带有 "success/fail" 代码的数组,然后是 lastInsertId。
该函数到目前为止一直运行良好,考虑到我遇到的错误(而且我是 PDO 的新手)我想知道问题出在哪里。
首先,函数本身非常良性(常量在别处定义)
function dbConnect(){
try {
if(!defined('PDO::ATTR_DRIVER_NAME')){
debugPrint('Error: PDO unavailable');
return false;
}
$dsn = 'mysql:host=' . DB_SERVER . ';dbname=' . DB_NAME . ';charset=UTF8';
$opt = [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
];
$db = new PDO($dsn, DB_USER, DB_PASS, $opt);
return $db;
} catch (PDOException $e) {
debugPrint($e->getMessage());
return false;
}
}
function dbInsert($sql, $param){
$ret = [];
if(!$pdo = dbConnect()){
$ret[] = 1;
return $ret;
}
if(!$stmt = $pdo->prepare($sql){
echo $pdo->errorInfo();
$ret[] = 1;
return $ret;
}
if(!$stmt->execute($param)){
echo $pdo->errorInfo();
$ret[] = 1;
return $ret;
} else {
$ret[] = 0;
$ret[1] = $pdo->lastInsertId();
return $ret;
}
我正在编写的代码只是检查电子邮件地址是否出现在 table 中,如果没有出现,则将其插入。我在我的应用程序中做了很多检查,大约 50% 的时间提供的电子邮件将是现有条目。
$sql = 'insert into customer (email) select :email from DUAL where not exists (select 1 from customer where email = :email)';
$bind = [':email' => 'user@bogus.org'];
这是迄今为止我见过的最简洁的方法,它是对数据库的一次调用并使用准备好的语句。但是,我收到 PDO 错误 "Invalid parameter number" 和堆栈跟踪错误。我已经打开 MySQL 日志记录,看来准备工作正常...
Prepare insert into customer (email) select ? from DUAL where not exists (select 1 from customer where email = ?)
...所以我只能认为这是绑定,考虑到参数错误的数量,这很有意义,但异常似乎来自执行:
PHP Fatal error: Uncaught PDOException: SQLSTATE[HY093]: Invalid parameter number in {filename}:78
Stack trace:
#0 {filename}:78: PDOStatement->execute(Array)
#1 {callingFile(33)}: dbInsert('insert into cus...', Array)
#2 {main}
thrown in {filename} on line 78
由于我是 PDO 的新手并且渴望学习,我想知道我应该在哪里寻找解决这个问题的方法。
我喜欢 PDO 的功能,我希望这不是一个需要解决的大问题,但目前我不知道为什么会出现此错误抛出。我确实想知道是不是因为我用的是“1”
注意:1 我知道我可以使电子邮件在 table 中唯一,但这意味着 $pdo->execute returns为非零,使我的函数 return 成为非零(错误)(至少在我看来)并非严格如此。
注意:2 我也知道我可以先检查 table 以查看电子邮件是否存在,如果不存在则插入:但是,我不确定这是最好的做事方式(并且由于我正在更新的代码目前正在这样做,我认为让它成为 PDO'ed 以及可能对 SQL 调用更聪明他们自己)。
注意:3 我也将 SQL 语句更改为以下内容:
$sql = 'insert ignore into customer (email) values (:email)';
$bind = [':email' => 'user@bogus.org'];
这是 "OK",但是如果存在现有电子邮件,则收件人 table 中的 auto_incremented 主键将跳过(再次尝试保持一切干净、简单和合乎逻辑的),我真的不想通过附加一个(慢速)语句来改变 table 以将 auto_increment 设置为 1
来解决这个问题1) 在你的函数 dbInsert
中,你错过了 if
语句的最后一个括号:
if (!$stmt = $pdo->prepare($sql)) {...}
2) 如 PDO::prepare 的官方文档所述:
You cannot use a named parameter marker of the same name more than once in a prepared statement, unless emulation mode is on.
例如不允许在 sql 语句中多次使用 :email
,除非您激活 ATTR_EMULATE_PREPARES
选项:
PDO::ATTR_EMULATE_PREPARES => TRUE
如果不这样做,您会收到与您所提供的错误完全相同的信息。
因此,要么应用上面的建议,要么让仿真保持不变(例如 FALSE
)并使用两个不同的命名参数标记(:email1
和 :email2
):
$email = 'user@bogus.org';
$sql = 'insert into customer (email) select :email1 from DUAL where not exists (select 1 from customer where email = :email2)';
$bind = [
':email1' => $email,
':email2' => $email,
];
或者您可以使用问号 (?) 参数标记:
$email = 'user@bogus.org';
$sql = 'insert into customer (email) select ? from DUAL where not exists (select 1 from customer where email = ?)';
$bind = [
1 => $email,
2 => $email,
];
作为非常好的资源,我向您推荐 this 教程。