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 年前
希望這能為大家節省時間:在 $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'
);
// 取代:
// $sth->execute(array(':calories' => $calories, ':colour' => $colour));
// 這也同樣適用:
$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]:
*** 無效的參數編號:綁定變數的數量與 token 的數量不符...
*
*/
// 這裡有錯字 ------------------------ |
// V
$sql = "UPDATE my_table SET fname = ?; lname = ? WHERE id = ?"; // 一個有效的 token
$pdos = $pdo->prepare($sql);
$pdos->execute( [ 'foo', 'bar', 3 ] ); // 三個 input_parameters
?>

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 不行。相反,傳遞一些「falsey」的值,例如整數 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 伺服器版本相對應的手冊,以了解正確的語法,以便在第 1 行附近使用 ''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.dev.org.tw/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 給定的變數包在引號中,則不會替換這些變數,例如

<?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.

我發現我有一個重複的 init 命令

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