mysqli_stmt::prepare

mysqli_stmt_prepare

(PHP 5, PHP 7, PHP 8)

mysqli_stmt::prepare -- mysqli_stmt_prepare准备执行 SQL 语句

描述

面向对象风格

public mysqli_stmt::prepare(string $query): bool

过程式风格

mysqli_stmt_prepare(mysqli_stmt $statement, string $query): bool

准备执行语句。查询必须包含单个 SQL 语句。

语句模板可以包含零个或多个问号 (?) 参数标记,也称为占位符。参数标记必须使用 mysqli_stmt_bind_param() 绑定到应用程序变量,然后才能执行语句。

注意:

在将比服务器 max_allowed_packet 更长的语句传递给 mysqli_stmt_prepare() 的情况下,返回的错误代码因您是否使用 MySQL Native Driver (mysqlnd) 或 MySQL Client Library (libmysqlclient) 而异。行为如下

  • mysqlnd 在 Linux 上返回错误代码 1153。错误消息表示 收到比 max_allowed_packet 字节更大的数据包

  • mysqlnd 在 Windows 上返回错误代码 2006。此错误消息表示 服务器已断开

  • libmysqlclient 在所有平台上返回错误代码 2006。此错误消息表示 服务器已断开

参数

statement

仅过程式风格:由 mysqli_stmt_init() 返回的 mysqli_stmt 对象。

query

查询,以字符串形式。它必须包含单个 SQL 语句。

SQL 语句可以包含零个或多个参数标记,用问号 (?) 字符在适当的位置表示。

注意:

标记仅在 SQL 语句的某些位置有效。例如,它们允许在 INSERT 语句的 VALUES() 列表中(为行指定列值),或者在与 WHERE 子句中的列进行比较以指定比较值。但是,它们不允许用于标识符(例如表名或列名)。

返回值

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

错误/异常

如果启用了 mysqli 错误报告 (MYSQLI_REPORT_ERROR) 并且请求的操作失败,则会生成警告。此外,如果模式设置为 MYSQLI_REPORT_STRICT,则会抛出 mysqli_sql_exception

示例

示例 #1 mysqli_stmt::prepare() 示例

面向对象风格

<?php

mysqli_report
(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");

$city = "Amersfoort";

/* 创建一个准备好的语句 */
$stmt = $mysqli->stmt_init();
$stmt->prepare("SELECT District FROM City WHERE Name=?");

/* 绑定参数以标记 */
$stmt->bind_param("s", $city);

/* 执行查询 */
$stmt->execute();

/* 绑定结果变量 */
$stmt->bind_result($district);

/* 获取值 */
$stmt->fetch();

printf("%s is in district %s\n", $city, $district);

过程式风格

<?php

mysqli_report
(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$link = mysqli_connect("localhost", "my_user", "my_password", "world");

$city = "Amersfoort";

/* 创建一个准备好的语句 */
$stmt = mysqli_stmt_init($link);
mysqli_stmt_prepare($stmt, "SELECT District FROM City WHERE Name=?");

/* 绑定参数以标记 */
mysqli_stmt_bind_param($stmt, "s", $city);

/* 执行查询 */
mysqli_stmt_execute($stmt);

/* 绑定结果变量 */
mysqli_stmt_bind_result($stmt, $district);

/* 获取值 */
mysqli_stmt_fetch($stmt);

printf("%s is in district %s\n", $city, $district);

以上示例将输出

Amersfoort is in district Utrecht

参见

添加说明

用户贡献说明 9 说明

logos-php at kith dot org
12 年前
请注意,如果您使用问号作为字符串值的占位符,则无需在 MySQL 查询中用引号将其括起来。

例如,执行此操作

mysqli_stmt_prepare($stmt, "SELECT * FROM foo WHERE foo.Date > ?");

不要执行此操作

mysqli_stmt_prepare($stmt, "SELECT * FROM foo WHERE foo.Date > '?'");

如果在查询中的问号周围加上引号,则 PHP 不会将问号识别为占位符,然后当尝试使用 mysqli_stmt_bind_param() 时,它会给出有关参数数量错误的错误。

此页面上的官方示例中隐式地缺少字符串占位符周围的引号,但文档中没有明确说明,我很难弄清楚,因此我认为有必要发布。
logos-php at kith dot orgpp
12 年前
事实证明,您不能直接使用预处理语句来执行在 IN() 子句中包含占位符的查询。

有一些解决方法(例如,构建一个由 n 个问号(以逗号分隔)组成的字符串,然后在 IN() 子句中使用该占位符集),但您不能只说 IN (?)。

这是一个 MySQL 限制,而不是 PHP 限制,但它在 MySQL 文档中也没有记录,因此我认为有必要在这里提及。

(顺便说一下,事实证明,其他人以前发布了我之前评论中提到的信息,关于不要使用引号。对于重复表示歉意;不知道我怎么会错过之前的评论。)
andrey at php dot net
18 年前
如果选择 LOB,请使用以下执行顺序,否则可能会导致 mysqli 分配超出实际使用的内存

1)prepare()
2)execute()
3)store_result()
4)bind_result()

如果您跳过 3) 或交换 3) 和 4),则 mysqli 将为该列的最大长度分配内存,对于 tinyblob 为 255,对于 blob 为 64k(仍然可以接受),对于 MEDIUMBLOB 为 16MByte(相当多)以及对于 LONGBLOB 为 4G(如果您有这么多内存,则很好)。使用此顺序的查询在存在 LOB 时速度稍慢,但这是避免在几秒钟内出现内存耗尽的代价。
kontakt at arthur minus schiwon dot de
16 年前
如果将占位符用引号括起来,您将遇到类似“准备好的语句中的变量数量与参数数量不匹配”(至少对于 INSERT 语句而言)的警告。
ndungi at gmail dot com
15 年前
`prepare`、`bind_param`、`bind_result`、`fetch` 结果、`close` stmt 循环有时会很繁琐。以下是一个对象,当您只需要一个 select 时,它会为您处理所有 mysqli 的繁琐工作,让您只需要在预处理的 stmt 上进行基本操作 `preparedSelect`。该方法将结果集作为二维关联数组返回,其中 `select`ed 列作为键。我没有进行充分的错误检查,也可能存在一些错误。帮助调试并改进它。

我使用了来自 http://www.biblesql.net/sites/biblesql.net/files/bible.mysql.gz. 的 bible.sql db。

巴拉卡 电话!

============================

<?php

class DB
{
public
$connection;

#建立数据库连接
public function __construct($host="localhost", $user="user", $pass="", $db="bible")
{
$this->connection = new mysqli($host, $user, $pass, $db);

if(
mysqli_connect_errno())
{
echo(
"数据库连接错误: "
. mysqli_connect_error($mysqli));
}
}

#存储 mysqli 对象
public function connect()
{
return
$this->connection;
}

#运行预处理查询
public function runPreparedQuery($query, $params_r)
{
$stmt = $this->connection->prepare($query);
$this->bindParameters($stmt, $params_r);

if (
$stmt->execute()) {
return
$stmt;
} else {
echo(
"在 $statement 中发生错误: "
. mysqli_error($this->connection));
return
0;
}

}

# 要使用绑定参数和绑定结果来运行 select 语句。
# 返回一个关联数组二维数组,您可以使用数组函数轻松地
# 进行操作。

public function preparedSelect($query, $bind_params_r)
{
$select = $this->runPreparedQuery($query, $bind_params_r);
$fields_r = $this->fetchFields($select);

foreach (
$fields_r as $field) {
$bind_result_r[] = &${$field};
}

$this->bindResult($select, $bind_result_r);

$result_r = array();
$i = 0;
while (
$select->fetch()) {
foreach (
$fields_r as $field) {
$result_r[$i][$field] = $$field;
}
$i++;
}
$select->close();
return
$result_r;
}


#接受绑定参数数组并将它们绑定到
#执行的预处理 stmt 的结果

private function bindParameters(&$obj, &$bind_params_r)
{
call_user_func_array(array($obj, "bind_param"), $bind_params_r);
}

private function
bindResult(&$obj, &$bind_result_r)
{
call_user_func_array(array($obj, "bind_result"), $bind_result_r);
}

#返回选定字段名称的列表

private function fetchFields($selectStmt)
{
$metadata = $selectStmt->result_metadata();
$fields_r = array();
while (
$field = $metadata->fetch_field()) {
$fields_r[] = $field->name;
}

return
$fields_r;
}
}
#类结束

#DB 类使用的示例

$DB = new DB("localhost", "root", "", "bible");
$var = 5;
$query = "SELECT abbr, name from books where id > ?" ;
$bound_params_r = array("i", $var);

$result_r = $DB->preparedSelect($query, $bound_params_r);

#循环遍历结果数组并显示结果

foreach ($result_r as $result) {
echo
$result['abbr'] . " : " . $result['name'] . "<br/>" ;
}

?>
mhradek AT gmail.com
16 年前
该函数和 call_user_func_array 函数的特别有用的改编

// $params 作为数组($val=>'i',$val=>'d' 等)发送

function db_stmt_bind_params($stmt, $params)
{
$funcArg[] = $stmt;
foreach($params as $val=>$type)
{
$funcArg['type'] .= $type;
$funcArg[] = $val;
}
return call_user_func_array('mysqli_stmt_bind_param', $funcArgs);
}

感谢 'sned' 提供的代码。
st dot john dot johnson at gmail dot com
17 年前
关于 lachlan76 之前所说的话,只要您告诉数据库在再次执行之前移动到下一个结果,存储过程就可以通过预处理语句执行。

示例(对存储过程的五次调用)

<?php
for ($i=0;$i<5;$i++) {
$statement = $mysqli->stmt_init();
$statement->prepare("CALL some_procedure( ? )");

// 绑定、执行和绑定结果。
$statement->bind_param("i", 1);
$statement->execute();
$statement->bind_result($results);

while(
$statement->fetch()) {
// 对结果进行处理。
}

$statement->close();

// 将 MySQLi 连接移至下一个结果。
while($mysqli->next_result()) { }
}
?>

如果包含最后一条语句,这段代码应该可以执行,不会出现恼人的“命令不同步”错误。
lukaszNOSPAMPLEASE at epas dot pl
16 年前
如果你还没有发现,我有一些坏消息要告诉你。
使用 mysqli_next_result() 的技巧只能防止在存储过程调用后连接断开。
显然,你可以为准备好的存储过程调用绑定参数,但你将在 mysqli_stmt_bind_result() 之后从 mysqli_stmt_fetch() 获取错误的记录,至少当存储过程本身包含准备好的语句时是这样。
为了避免数据损坏,可以指定 mysqli_real_connect() 中的 CLIENT_MULTI_STATEMENTS 标志,如果它没有被完全禁用(出于安全原因,他们这么说)。另一个选择是使用 mysqli_multi_query(),但这样你就无法绑定参数。
lachlan76 at gmail dot com
17 年前
不要尝试通过准备好的语句使用存储过程。

示例

<?php
$statement
= $mysqli->stmt_init();
$statement->prepare("CALL some_procedure()");
?>

如果你尝试这样做,它会失败,并在下一个查询期间断开连接。请使用 mysqli_multi_query 代替。

示例

<?php
$mysqli
->multi_query("CALL some_procedure()");
do
{
$result = $mysqli->store_result();

// 在这里进行处理工作

$result->free();
} while(
$mysqli->next_result());
?>

这意味着你无法绑定参数或结果。
To Top