登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  php教程

PHP mysqli::execute_query 怎样绑定参数并读取结果

来源:17golang原创

时间:2026-11-03 09:19:20 419浏览 收藏

PHP 8.2 及以上可以用 mysqli::execute_query() 把“带问号占位符的 SQL、参数数组、执行”合并成一次调用。最重要的对应关系只有一个:SQL 中有几个 ?,参数数组就按同样顺序提供几个值;查询类语句返回 mysqli_result,写入类语句则返回布尔结果,再从连接对象读取 affected_rows。

官方文档:https://www.php.net/manual/en/mysqli.execute-query.php

要点速览
  • mysqli::execute_query() 是快捷入口,内部覆盖预处理、绑定、执行和结果获取这条常见路径。
  • 参数数组只对应问号占位符,不要把用户输入直接拼到 SQL 字符串里。
  • SELECT 通常得到 mysqli_result;INSERT、UPDATE、DELETE 通常用布尔返回值和 affected_rows 判断。

先认清 execute_query 的职责

mysqli::execute_query($query, $params) 是 PHP 8.2 引入的便捷方法。它接收一个 mysqli 连接、带零个或多个问号占位符的 SQL 模板,以及可选参数数组;官方手册将它描述为对 prepare()、bind_param()、execute() 和 get_result() 常见组合的封装。

它解决的是“这一次查询怎样把值绑定进去并拿到结果”,不是连接池、事务编排或 SQL 生成器。也就是说,连接对象应该已经准备好,表名和列名等结构仍然要由程序自己的白名单或固定代码决定。

set_charset('utf8mb4'); // 让连接字符集与业务文本保持一致。

$sql = 'SELECT id, email FROM users WHERE status = ? LIMIT ?';
$rows = $mysqli->execute_query($sql, ['active', 20]);

// SELECT 得到结果集;读取完后释放结果对象。
while ($row = $rows->fetch_assoc()) {
    echo $row['id'] . ':' . $row['email'] . PHP_EOL;
}
$rows->free();
$mysqli->close(); // 示例结束后释放连接资源。
?>

这段代码里有两个问号,所以参数数组有两个元素。LIMIT ? 是否被当前数据库驱动和 SQL 方言接受,要以项目的实际数据库版本和测试环境为准;如果使用的数据库对某个位置有特殊限制,应改成明确的 SQL 分支,而不是把结构片段塞进参数数组。

PHP mysqli execute_query 中连接、SQL 模板、问号占位符、参数数组和预处理语句的静态关系说明图
图1:参数关系说明图,展示 SQL 模板、问号占位符、参数数组和预处理语句的静态对应关系,不是运行截图。

用问号占位符传入参数

参数绑定的核心不是“把字符串替换到 SQL 中”,而是把 SQL 模板和参数值分开交给 MySQLi。比如按邮箱查一条用户记录,可以让 SQL 保持固定,只把邮箱放在参数数组里:

execute_query($sql, [$email]);
$user = $result->fetch_assoc();
$result->free();

if ($user === null) {
    // fetch_assoc 在没有下一行时返回 null,按业务语义处理“未找到”。
    echo '用户不存在' . PHP_EOL;
} else {
    echo '用户编号:' . $user['id'] . PHP_EOL;
}
?>

不要用参数占位符替换表名、列名或排序关键字。占位符适合数据值;结构选择应由固定映射控制,例如把前端的排序选项映射为预先写好的 ORDER BY created_at 或 ORDER BY id,再拼接已经经过白名单确认的片段。

参数数量不一致时,问题应尽早暴露。实际项目可以在组装 SQL 和参数数组的同一处维护它们,避免一个函数临时增加问号,另一个函数忘记补参数。

按结果类型读取查询结果

判断返回值时,先看 SQL 的业务类型。SELECT、部分存储过程或返回结果集的语句,通常要把返回值当作 mysqli_result 使用;INSERT、UPDATE、DELETE 等写入语句更适合先判断布尔结果,再读取连接对象的受影响行数。

execute_query(
    'SELECT id, title FROM posts WHERE author_id = ? ORDER BY id DESC',
    [$authorId]
);

$posts = [];
while ($post = $result->fetch_assoc()) {
    $posts[] = $post;
}
$result->free(); // 结果集不再使用后及时释放。

// 写入语句返回 bool;受影响行数从 mysqli 连接读取。
$updated = $mysqli->execute_query(
    'UPDATE posts SET published = ? WHERE id = ?',
    [1, $postId]
);
if ($updated) {
    $changed = $mysqli->affected_rows;
    echo "更新行数:{$changed}" . PHP_EOL;
}
?>

对更新语句来说,执行成功不等于一定改了数据。条件没有命中时,affected_rows 可能为零;某些客户端配置还会影响“匹配行”和“实际改变行”的统计语义,所以业务上要先确定你要记录的是哪一种含义。

PHP mysqli execute_query 的 SELECT 结果集、fetch_assoc、写入布尔结果和 affected_rows 静态分支说明图
图2:结果边界说明图,展示查询结果集与写入结果的静态分支,不是执行结果或数据库控制台截图。

处理空结果、异常和受影响行

建议把“执行失败”“执行成功但没有记录”“执行成功且有记录”分开。开启严格报告后,SQL 错误会以异常形式暴露;如果项目选择手动检查返回值,也要把错误信息和业务提示分开,避免把敏感连接信息直接回显给用户。

execute_query(
        'SELECT id, name FROM teams WHERE slug = ?',
        [$slug]
    );

    $team = $result->fetch_assoc();
    $result->free();
    if ($team === null) {
        // “没有匹配行”不是 SQL 执行失败,交给业务层决定返回 404 或空状态。
        return null;
    }
    return $team;
} catch (mysqli_sql_exception $exception) {
    // 记录内部错误上下文,但不要把 SQL、账号或密码原样输出给用户。
    error_log('team query failed: ' . $exception->getMessage());
    throw $exception;
}
?>

空结果不应该靠异常判断。异常表示执行路径出现错误;fetch_assoc() 返回 null 则表示结果集已经没有下一行。把这两个状态分开,接口层才能稳定地区分“资源不存在”和“数据库暂时不可用”。

与手动 prepare 做场景选择

execute_query() 适合一次性的短查询:SQL 模板明确、参数数量固定、结果读取逻辑紧邻调用点。若同一个预处理语句需要循环执行,或者需要使用绑定类型、逐行绑定结果、显式控制 statement 生命周期,手动 prepare() 往往更清楚。

场景更适合判断依据
一次查询,参数数组已准备好execute_query()代码短,参数与 SQL 放在同一处
同一模板循环执行手动 prepare()准备一次,反复绑定和执行
需要细粒度读取 statement 状态手动 mysqli_stmt生命周期与错误分支更显式
只想判断是否更新成功execute_query() + affected_rows无需暴露完整 statement 对象

无论选择哪条路径,参数值和 SQL 结构都要分开。快捷方法减少的是样板代码,不是对输入边界的判断;表名、字段名、排序方向等结构化选项仍需固定映射。

常见问题

mysqli::execute_query 从哪个 PHP 版本开始可用?

PHP 手册标注它从 PHP 8.2.0 开始提供。如果项目需要兼容更旧的 PHP,应使用手动 prepare()、bind_param() 和 execute() 组合。

参数数组能替换表名吗?

不能。问号占位符用于数据值,不用于表名、列名或 SQL 关键字。结构选项要用固定映射或白名单决定。

SELECT 没有结果是执行失败吗?

不是。执行成功后,结果集的 fetch_assoc() 没有下一行会返回 null;真正的执行错误应通过异常或明确的错误返回分支处理。

UPDATE 成功但 affected_rows 为零怎么理解?

可能是条件没有匹配行,也可能是匹配行的值没有发生变化。具体统计语义要结合客户端配置和业务目标解释,不能只把零当作异常。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>