我正在使用一个正在处理我的代码的函数,我正在慢慢地从MySQLi转移到PDO - 这个函数应该“简单地”获取一个SQL语句和一组变量并设置预处理语句,执行它并返回一个带有“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;
}
我正在编写的代码只是检查一个电子邮件地址是否出现在表格中,如果没有,请插入它。我在我的应用程序中检查了很多,并且大约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错误“无效参数编号”和堆栈跟踪错误。我已经启用了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 我知道我可以在表格中使电子邮件唯一,但这意味着$ pdo->执行返回非零,使我的函数返回非零(错误)(至少在我看来)并非严格属实。
注意:2 我也知道我可以先检查一下表格,看看是否有电子邮件,如果不存在则插入它:但是,我不确定这是最好的做事的方式(并且正如我正在更新的代码所做的那样,我认为它是PDO'以及可能对SQL调用本身更聪明)。
注意:3 我还将SQL语句更改为以下内容:
$sql = 'insert ignore into customer (email) values (:email)';
$bind = [':email' => 'user@bogus.org'];
这是“OK”,但是如果现有的电子邮件存在,那么收件人表中的auto_incremented主键会跳过(同样,试图保持一切干净,简单和合乎逻辑),我真的不想破解这个通过附加一个(慢)语句来改变表,将auto_increment设置为1
答案 0 :(得分:1)
1)在你的函数dbInsert
中,你错过了if
语句的最后一个括号:
if (!$stmt = $pdo->prepare($sql)) {...}
<小时/> 2)正如PDO::prepare的官方文档中所述:
您不能使用同名的命名参数标记 一旦在准备好的声明中,除非启用仿真模式。
E.g。除非您激活:email
选项,否则不允许在sql语句中多次使用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教程。