PHP Conference Japan 2024

PDOStatement::execute

(PHP 5 >= 5.1.0, PHP 7, PHP 8, PECL pdo >= 0.1.0)

PDOStatement::execute 执行预处理语句

描述

public PDOStatement::execute(?array $params = null): bool

执行预处理语句。如果预处理语句包含参数标记,则需要

  • PDOStatement::bindParam() 和/或 PDOStatement::bindValue() 来绑定变量或值(分别)到参数标记。绑定的变量将它们的值作为输入传递,并接收与它们关联的参数标记的输出值(如果有)。

  • 或者必须传递一个仅输入参数值的数组。

参数

params

一个值数组,其元素个数与正在执行的 SQL 语句中绑定的参数个数相同。所有值都被视为PDO::PARAM_STR

多个值不能绑定到单个参数;例如,不允许将两个值绑定到 IN() 子句中的单个命名参数。

无法绑定比指定数量更多的值;如果params中的键比PDO::prepare()中指定的SQL中指定的键多,则语句将失败并发出错误。

返回值

成功时返回true,失败时返回false

错误/异常

如果属性PDO::ATTR_ERRMODE设置为PDO::ERRMODE_WARNING,则发出级别为E_WARNING的错误。

如果属性PDO::ATTR_ERRMODE设置为PDO::ERRMODE_EXCEPTION,则抛出PDOException

示例

示例 #1 使用绑定的变量和值执行预处理语句

<?php
/* 使用绑定的变量和值执行预处理语句 */
$calories = 150;
$colour = 'gre';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour LIKE :colour'
);
$sth->bindParam('calories', $calories, PDO::PARAM_INT);
/* 名称也可以用冒号 ":" 开头(可选) */
$sth->bindValue(':colour', "%$colour%");
$sth->execute();
?>

示例 #2 使用命名值的数组执行预处理语句

<?php
/* 通过传递插入值的数组来执行预处理语句 */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour'
);
$sth->execute(array('calories' => $calories, 'colour' => $colour));
/* 数组键也可以用冒号 ":" 开头(可选) */
$sth->execute(array(':calories' => $calories, ':colour' => $colour));
?>

示例 #3 使用位置值的数组执行预处理语句

<?php
/* 通过传递插入值的数组来执行预处理语句 */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < ? AND colour = ?'
);
$sth->execute(array($calories, $colour));
?>

示例 #4 使用绑定到位置占位符的变量执行预处理语句

<?php
/* 使用绑定的 PHP 变量执行预处理语句 */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < ? AND colour = ?'
);
$sth->bindParam(1, $calories, PDO::PARAM_INT);
$sth->bindParam(2, $colour, PDO::PARAM_STR, 12);
$sth->execute();
?>

示例 #5 使用数组为 IN 子句执行预处理语句

<?php
/* 使用值数组执行预处理语句,用于 IN 子句 */
$params = array(1, 21, 63, 171);
/* 创建一个参数占位符字符串,填充到参数数量 */
$place_holders = '?' . str_repeat(', ?', count($params) - 1);

/*
这将使用足够的未命名占位符来准备语句,以用于$params数组中的每个值。
然后,当执行语句时,$params数组的值将绑定到预处理语句中的占位符。
这与使用PDOStatement::bindParam()不同,因为这需要对变量的引用。
PDOStatement::execute() 只绑定值。
*/
$sth = $dbh->prepare("SELECT id, name FROM contacts WHERE id IN ($place_holders)");
$sth->execute($params);
?>

注释

注意:

某些驱动程序需要在执行下一个语句之前关闭游标

参见

添加注释

用户贡献的注释 26 条注释

Jean-Lou dot Dupont at jldupont dot com
16 年前
希望这能为各位节省时间:为了真正确定诸如“update”或“replace”之类的操作是否成功(即更改了一些数据),应该在$stmt->execute()之后使用$count = $stmt->rowCount()。

Jean-Lou Dupont。
gx
14 年前
请注意,您必须
- 要么将所有值作为数组传递给 PDOStatement::execute()
- 要么使用 PDOStatement::bindValue() 预先绑定每个值,然后使用 *无* 参数(甚至不是 "array()"!)调用 PDOStatement::execute()。
将数组(空或非空)传递给 execute() 将“擦除”并替换任何之前的绑定(例如,对于 MySQL,如果传递空数组,可能会导致“SQLSTATE[HY000]: General error: 2031”(CR_PARAMS_NOT_BOUND)。

因此,如果预处理语句之前已被“绑定”,则以下函数不正确

<?php
function customExecute(PDOStatement &$sth, $params = NULL) {
return
$sth->execute($params);
}
?>

因此应替换为类似以下内容:

<?php
function customExecute(PDOStatement &$sth, array $params = array()) {
if (empty(
$params))
return
$sth->execute();
return
$sth->execute($params);
}
?>

另请注意,PDOStatement::execute() 不需要 $input_parameters 为数组。

(当然,不要按原样使用 ^^)。
VolGas
17 年前
插入值的数组(命名参数)不需要前缀冒号作为键值对才能工作。

<?php
/* 通过传递插入值的数组来执行预处理语句 */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour'
);
// instead of:
// $sth->execute(array(':calories' => $calories, ':colour' => $colour));
// this works fine, too:
$sth->execute(array('calories' => $calories, 'colour' => $colour));
?>

这允许使用“常规”组装的哈希表(数组)。
这确实很有意义!
ElTorqiro
13 年前
当使用预处理语句执行多个插入(例如在循环中)时,在 sqlite 下,通过将循环包装在事务中可以显著提高性能。

我的应用程序经常一次插入 30,000-50,000 条记录。不使用事务需要 150 多秒,而使用事务只需要 3 秒。

这也可能影响其他实现,我相信它在某种程度上会影响所有数据库,但我只能使用 PDO sqlite 进行测试。

例如:

<?php
$data
= array(
array(
'name' => 'John', 'age' => '25'),
array(
'name' => 'Wendy', 'age' => '32')
);

try {
$pdo = new PDO('sqlite:myfile.sqlite');
}

catch(
PDOException $e) {
die(
'Unable to open database connection');
}

$insertStatement = $pdo->prepare('insert into mytable (name, age) values (:name, :age)');

// 开始事务
$pdo->beginTransaction();

foreach(
$data as &$row) {
$insertStatement->execute($row);
}

// 结束事务
$pdo->commit();

?>

[由 sobak 编辑:Pere 于 2014 年 9 月 12 日 01:07 提交的错别字修正]
Sbastien
1 年前
奇怪的是,手册没有给出完整的 SELECT 示例。

<?php

$sql
= <<<SQL
SELECT ALL name, calories, colour
FROM fruit
WHERE calories < :calories AND colour = :colour
SQL;

$select = $pdo->prepare($sql);

$select->execute(['calories' => 150, 'colour' => 'red']);

$data = $select->fetchAll();
albright atat anre dotdot net
16 年前
当查询包含问号时,将值数组传递给 execute 时,请注意数组必须从零开始按数字键索引。如果不是,请对其运行 array_values() 以强制重新索引数组。

<?php
$anarray
= array(42 => "foo", 101 => "bar");
$statement = $dbo->prepare("SELECT * FROM table WHERE col1 = ? AND col2 = ?");

// 这将不起作用
$statement->execute($anarray);

// 执行此操作以使其工作
$statement->execute(array_values($anarray));
?>
Ray.Paseur sometimes uses Gmail
8 年前
“您不能绑定比指定的更多值;如果 input_parameters 中的键数多于 PDO::prepare() 中指定的 SQL 中的键数,则语句将失败并发出错误。” 但是较少的键可能不会导致错误。

只要查询字符串变量中问号的数量与 input_parameters 中元素的数量匹配,就会尝试执行查询。

即使在查询字符串末尾之后存在多余的信息,也会发生这种情况。分号表示查询字符串的结尾;SQL 引擎将变量的其余部分视为注释,但 PHP 将其计入 input_parameters。

请查看这两个查询字符串。唯一的区别是第二个字符串中的一个错字,其中分号意外地替换了逗号。此 UPDATE 查询将运行,将应用于所有行,并将静默损坏表。

<?php
/**
* 此查询旨在根据 WHERE 子句更新部分行
*/
$sql = "UPDATE my_table SET fname = ?, lname = ? WHERE id = ?";

/**
* 此查询更新所有行,忽略分号后面的所有内容,包括 WHERE 子句!
*
* 预期结果(但未收到):
*
*** 警告:
*** PDOStatement::execute():
*** SQLSTATE[HY093]:
*** 绑定变量数量与标记数量不匹配...
*
*/
// 此处有错别字 ------------------------ |
// V
$sql = "UPDATE my_table SET fname = ?; lname = ? WHERE id = ?"; // 实际上只有一个标记
$pdos = $pdo->prepare($sql);
$pdos->execute( [ 'foo', 'bar', 3 ] ); // 三个输入参数
?>

PHP 5.4.45,mysqlnd 5.0.10
Rami jamleh
11年前
简化的 $placeholder 形式

<?php

$data
= ['a'=>'foo','b'=>'bar'];

$keys = array_keys($data);
$fields = '`'.implode('`, `',$keys).'`';

#我的方法
$placeholder = substr(str_repeat('?,',count($keys)),0,-1);

$pdo->prepare("INSERT INTO `baz`($fields) VALUES($placeholder)")->execute(array_values($data));
anon at anon dot com
12年前
如果你的MySQL表有500,000+行,并且你的脚本因为达到PHP的内存限制而失败,请设置以下属性。

<?php $this->pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); ?>

这应该可以消除错误并使内存使用恢复正常。
danny dot panzer at gmail dot com
3年前
我发现PostgreSQL有一些非常奇怪的行为

在事务之外,你可以将布尔值 true/false 作为输入数组的成员,它似乎可以工作。

但是,*在*事务*内部*,布尔值 true 可以工作,但布尔值 false 不行。相反,请传递类似整数 0 或字符串 "false" 的“假值”。
simon dot lehmann at gmx dot de
17 年前
似乎不同版本之间的引用行为有所改变,因为我的当前项目在一个设置上运行良好,但在另一个设置上却抛出错误(这两个设置非常相似)。

设置1:Ubuntu 6.10,PHP 5.1.6,MySQL 5.0.24a
设置2:Ubuntu 7.04,PHP 5.2.1,MySQL 5.0.38

导致问题的代码片段(已缩短)
<?php
$stmt
= $pdo->prepare("SELECT col1, col2, col3 FROM tablename WHERE col4=? LIMIT ?");
$stmt->execute(array('Foo', 1));
?>

在第一个设置中,这段代码执行没有任何问题,但在第二个设置中会生成一个错误

SQLSTATE[42000]: 语法错误或访问冲突:1064 你的SQL语法有错误;请检查与你的MySQL服务器版本相对应的参考手册,以了解正确的语法。“near ''1'' at line 1”

问题在于,$stmt->execute() 会引用传递给第二个占位符的数字(结果为:... LIMIT '1'),这在 MySQL 中是不允许的(在两个设置上都经过测试)。

为了避免这种情况,你必须使用 bindParam() 或 bindValue() 并指定数据类型。
T-Rex
12年前
当你尝试使用日期进行查询时,请使用整个日期,而不仅仅是一个数字。

如果你这样尝试,这个查询会运行良好
SELECT * FROM table WHERE date = 0

但是如果你使用预处理语句,你必须使用完整的日期格式。
<?php
$sth
= $dbh->prepare('SELECT * FROM table WHERE date = :date');
$sth->execute( $arArray );

//--- 错误的:
$arArray = array(":date",0);

//--- 正确的:
$arArray = array(":date","0000-00-00 00:00:00");
?>

MySQL驱动程序肯定存在问题。

此致
T-Rex
Ihor Ivanov
7年前
如果缺少或拼写错误一个参数名,即使将PDO::ATTR_ERRMODE设置为PDO::ERRMODE_SILENT,此函数也会抛出E_WARNING级别的错误!
在相同的情况下,但当PDO::ERRMODE_WARNING设置时,此函数会抛出两个E_WARNING级别的错误!

当设置PDO::ERRMODE_EXCEPTION时,此函数不会抛出任何错误,而是抛出一个PDOException。

所有这些都适用于你使用PDOStatement::bindParam()函数并拼写错误参数名,然后使用PDOStatement::execute();的情况。

测试环境:Windows 10,PHP 5.5.35,mysqlnd 5.0.11,MySQL 5.6.30。

<?php
$dbh
->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);

$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE colour = :colour'
);

/*
注意参数名 ':color' 而不是 ':colour'。

当设置 PDO::ERRMODE_SILENT 时,此函数会抛出以下错误:
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: parameter was not defined in...

当设置 PDO::ERRMODE_WARNING 时,此函数会抛出以下两个错误:
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: parameter was not defined in...
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number in...
*/
$sth->execute(array(':color' => $colour));
?>
joshuag at count-recount at dynaggelos dot com
2年前
我花了很长时间才意识到这一点,而且文档似乎没有明确说明如何在 SELECT 语句类型的查询上使用 PDO_Statement::execute(),所以我在这里记录一下。当准备一个 SELECT 查询,然后使用 PDO_Statement::execute() 执行它时,你可以简单地继续在同一个 PDO_Statement 对象上使用 PDO_Statement::fetch() 或 PDO_Statement::fetchAll()。这与使用 PDO::query() 返回 PDO_Statement 对象,然后在该对象上调用 PDO_Statement::fetch() 没有什么不同。这是因为 PDO_Statement 对象当然仍然是 PDO_Statement 对象,并且,正如 PDO::query 文档 (https://php.net/manual/en/pdo.query.php) 所说,PDO::query 也会“[p]repares and executes an SQL statement.”

<?php

$pdo_statement
= $my_pdo_object->prepare( "SELECT * FROM `MyTable` WHERE `Field1` = 'this_string'" );

if (
true === $this->execute_safe_query( $pdo_statement ) ) {

echo
$pdo_statement->fetch();

}

?>
richard at securebucket dot com
13 年前
注意:参数名中不能使用连字符,例如 ":asd-asd",你可以使用 `str_replace("-","_",$parameter)` 快速修复这个问题。
Whitebeard
10年前
如果你在传递要绑定的布尔值时遇到问题,并且正在使用 Postgres 数据库……但你不想为每个参数都使用 bindParam,请尝试传递字符串 't' 或 'f' 而不是布尔值 TRUE 或 FALSE。
Robin Millette
13 年前
如果你要派生 PDOStatement 来扩展 execute() 方法,你必须使用默认 NULL 参数定义签名,而不是空数组。

换句话说
<?php
class MyPDOStatement extends PDOStatement {
// ...

// 不要使用这种形式!
// function execute($input_parameters = array()) {
// 使用这个代替:
function execute($input_parameters = null) {
// ...
return parent::execute($input_parameters);
}
}

?>

顺便说一句,这就是为什么我总是将默认参数设置为 NULL,并在方法或函数的主体中处理实际的正确默认参数。因此,当你必须使用所有参数调用函数时,你知道总是为默认值传递 NULL。
Ant P.
16 年前
在 5.2.6 版本中,你仍然不能使用此函数的 $input_parameters 向 PostgreSQL 传递布尔值。要做到这一点,你必须为查询中的每个参数调用 bindParam() 并指定显式类型。
Tony Casparro
14 年前
我们知道在数据库解析最终的原始SQL之前,你无法看到它,但如果你想模拟最终结果,这可能会有所帮助。

<?php
public function showQuery($query, $params)
{
$keys = array();
$values = array();

# 为每个参数构建正则表达式
foreach ($params as $key=>$value)
{
if (
is_string($key))
{
$keys[] = '/:'.$key.'/';
}
else
{
$keys[] = '/[?]/';
}

if(
is_numeric($value))
{
$values[] = intval($value);
}
else
{
$values[] = '"'.$value .'"';
}
}

$query = preg_replace($keys, $values, $query, 1, $count);
return
$query;
}
?>
mail at horn-online-media dot de
12年前
你好:

快速提示一下,使用引号时如何避免启动问题:如果PDO变量用引号括起来,PDO不会替换这些变量,例如:

<?php

$st
= $db->prepare( '
INSERT INTO fruits( name, colour )
VALUES( :name, ":colour" )
'
;
$st->execute( array( ':name' => 'Apple', ':colour' => 'red' ) );

?>

结果会产生一个新的水果,例如

-> Apple, :colour

颜色没有被“red”替换。所以请将变量放在引号**之外** - PDO 会处理。
davidhcefx
3年前
正如一些人指出的那样,**不要将使用名称作为键的数组传递给问号参数!**

<?php
$sth
= $dbh->prepare('INSERT INTO fruit (name, colour, colories) VALUES (?, ?, ?)');

// 这是错误的!
// $param = array("name" => "apple", "colour" => "red", "colories" => 150);

// 数组的键必须是从零开始的整数
$param = array("apple", "red", 150);
$sth->execute($param);
nils andre with my googelian maily accou
12年前
我发现当我从命令行调试我的PHP脚本时遇到了严重的麻烦,尽管使用了fetchAll,但我总是得到错误

SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active.

我意识到我有一个双重初始化命令

PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8; SET CHARACTER SET utf8;"

第一个是更好的选择,删除后者,错误消失了。
russel at sunraystudios dot com
18年前
我用过它,它返回布尔值=>
$passed = $stmt->execute();
if($passed){
echo "passed";
} else {
echo "failed";
}

如果语句失败,它将打印failed。你可能想使用errorInfo()获取更多信息,但它似乎对我有用。
mail at tinodidriksen dot com
8 年前
这个例子展示了如何生成所需数量的问号,这非常浪费
$place_holders = implode(',', array_fill(0, count($params), '?'));

相反,只需执行
$place_holders = '?'.str_repeat(',?', count($params)-1);
pere dot pasqual at gmail dot com
9年前
自从simon dot lehmann at gmx dot comment以来已经7年了,但今天我发现自己在一个涉及INSERT的预处理语句、用于Microsoft Access的PDO odbc驱动程序和PHP 5.4.7中遇到了问题。预处理语句是使用prepare + execute方法完成的,抛出一个难看的
"SQLExecDirect[-3500] at ext\\pdo_odbc\\odbc_driver.c:247" 错误
和一个
42000 (“语法错误或访问冲突”) SQLSTATE。

他怀疑问题是什么,并指出了一个可能的解决方案:使用bindParam()或bindValue()并指定数据类型。

好吧,这似乎正确地识别了问题的根源,但有一个更简单的解决方案对我有用,更简单,并且允许你继续使用pdo::prepare()作为参数和pdo::execute()
唯一需要做的就是,如果之前没有做过,在将绑定参数放入传递给pdo::execute($array)的数组之前,将其转换为其特定类型(数据库期望的类型)。

以下代码失败,抛出上述错误

<?php
$name
= "John";
$length = "1";
$price = "1.78";
$SQL = "INSERT INTO table (name, length, price) VALUES (?,?,?)";
$arra = array($name, $length, $price);
$sth = $msq->prepare($SQL);
$sth->execute($arra);
?>

这个对我很有效

<?php
$name
= "John";
$length = (int)"1"; // 数据库期望此类型
$price = (float)"1.78"; // 数据库期望此类型
$SQL = "INSERT INTO table (name, length, price) VALUES (?,?,?)";
$arra = array($name, $length, $price);
$sth = $msq->prepare($SQL);
$sth->execute($arra);
?>
valterekholm at hotmail.com
4年前
我在绑定时使用问号的方式。当我使用

$stmt->execute($values);

时,我尝试使用关联数组,其键为列名。我认为这在渲染查询时会更容易(因为我参数的数量是可变的)。

我发现execute无法使用关联数组,只能使用数值数组(具有数值索引)。
To Top