Mysql PDO准备的语句“无效参数号”
我正在使用一个函数,这个函数一直在处理我的代码,现在我正在慢慢地从MySQLi迁移到PDO——这个函数应该只需要一个SQL语句和一个变量数组,然后设置准备好的语句,执行它并返回一个带有成功/失败代码的数组,然后是lastInsertId 到目前为止,这个函数一直工作得很好,考虑到我遇到的错误以及我是PDO新手,我想知道问题出在哪里 首先,函数本身是在别处定义的非常友好的常量Mysql PDO准备的语句“无效参数号”,mysql,pdo,prepared-statement,Mysql,Pdo,Prepared Statement,我正在使用一个函数,这个函数一直在处理我的代码,现在我正在慢慢地从MySQLi迁移到PDO——这个函数应该只需要一个SQL语句和一个变量数组,然后设置准备好的语句,执行它并返回一个带有成功/失败代码的数组,然后是lastInsertId 到目前为止,这个函数一直工作得很好,考虑到我遇到的错误以及我是PDO新手,我想知道问题出在哪里 首先,函数本身是在别处定义的非常友好的常量 function dbConnect(){ try { if(!defined('PDO::ATT
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->execute返回为非零,使我的函数返回一个非零错误,至少在我看来不是严格正确的
注意:2我也知道我可以先检查表,看看电子邮件是否存在,如果不存在,再插入它:但是,我不确定这是做事情的最佳方式,因为我正在更新的代码目前正在做这件事,我的意见是使它PDO'ed,以及可能更聪明的SQL调用本身
注意:3我还将SQL语句更改为:
$sql = 'insert ignore into customer (email) values (:email)';
$bind = [':email' => 'user@bogus.org'];
这没问题,但是如果现有电子邮件再次出现,收件人表中自动递增的主键将跳过,以保持所有内容的整洁,简单且符合逻辑,我真的不想在函数dbInsert中添加一个缓慢的语句来改变表,将auto_increment设置为1,从而解决这个问题。您错过了if语句的最后一个括号:
if (!$stmt = $pdo->prepare($sql)) {...}
2如官方文件所述:
使用同名的命名参数标记的次数不能超过
在准备好的语句中执行一次,除非启用了仿真模式
例如,不允许在sql语句中多次使用:email,除非激活ATTR\u EMULATE\u PREPARES选项:
如果没有,您将收到您所呈现的错误
因此,要么应用上面的建议,要么让模拟保持不变,例如,设置为FALSE,并使用两个不同的命名参数标记:email1和:email2:
也可以使用问号参数标记:
$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,
];
作为一个非常好的资源,我向您推荐教程。Argh!我希望这是一件简单的事情,而我的经验不足会使它受到影响。非常感谢你的帮助,不客气。请注意,参数标记只应在sql语句中的某些位置使用。如果不在应该使用它们的地方使用它们,那么sql注入就无法不费吹灰之力地避免,或者根本无法避免。我记得,本教程也指出了这个问题。请允许我问:我已经设置了dbInsert调用dbConnect阻止我使用PDO事务,对吗?我有一个执行插入、选择、插入和最后选择的文件,该文件希望确保一切正常,应该是该文件生成$pdo->beginTransaction,最终生成$pso->commit,是吗?当然,目前它没有$pdo对象,因为它是在初始dbInsert中调用的,所以我在函数中调用dbConnect的想法需要删除,并从文件本身调用?@bnoeafk嗯,我从来没有处理过事务。但每次执行db操作CRUD时,绝对不应该创建db连接,例如pdo对象。相反,您应该只创建一个db连接对象,所有需要它的函数和类都应该共享它。例如,这个pdo对象应该作为参数注入函数或类构造函数中,或者在普通代码段中使用。在您的情况下,正如您已经正确建议的那样,您应该调用与任何函数分离的dbConnect。因此,我想这样做:@bnoeafk我将创建一个php文件connection.php,如果您愿意,在其中创建一个新的pdo对象$pdo,而不使用任何 功能。只有来自dbConnect的try-catch块。实际上,对于这样的操作,您根本不需要这样的异常处理,例如try catch块。阅读并了解原因和方式。然后我会在所有需要db操作的文件中包括connection.php文件,例如db连接。
$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,
];