mysqli_stmt::bind_param() - 为每个参数指定另一个数据类型而不是“s”

Posted

技术标签:

【中文标题】mysqli_stmt::bind_param() - 为每个参数指定另一个数据类型而不是“s”【英文标题】:mysqli_stmt::bind_param() - specify another data type than "s" for each parameter 【发布时间】:2018-12-10 19:36:09 【问题描述】:

mysqli_stmt 没有query_params() 函数,我必须自己编写。参数 arry 绑定到带有bind_param() 的语句。我需要动态指定变量类型。我可以这样做:

$sType = '';
foreach ($aParameters as $iIndex => $mParameter) 
  if     (is_string($mParameter)) $sType .= 's';
  elseif (   is_int($mParameter)) $sType .= 'i';
  elseif ( is_float($mParameter)) $sType .= 'd';
  elseif (  is_null($mParameter)) $sType .= 's';
  elseif (  is_bool($mParameter)) 
    $sType .= 'i';
    $aParameters[$iIndex] = boolval($mParameter);
  else 
    // trow new Exception(...);
  

但事实证明,mysql/mariadb 会将布尔值、整数和浮点数作为字符串发送,数据库服务器会很乐意将它们转换为列的相应数据类型。看来我可以跳过这一步,默认情况下将每个参数作为字符串发送。

是否有任何理由为每个参数指定除"s" 之外的其他数据类型?

编辑:我刚刚找到this SO 主题,该主题显示了当二进制数据包超过max_allowed_packet 设置时如何使用“b”类型和mysqli_stmt::send_long_data。我还读到它会提高使用bin2hex() 将字节字符串作为文本发送的解决方案的性能。

【问题讨论】:

绝对没有理由这样做。原因是如果您总是希望将特定参数绑定为整数或浮点数。根据要绑定的变量的数据类型来确定要绑定的类型对 MySQL 来说毫无意义。 将它们全部视为s 字符串,除非您在空引用上的某些b 二进制文件中进行管道处理。 @IncredibleHat 是的,到目前为止,这似乎是底线。谢谢! 【参考方案1】:

我发现使用整数参数很重要的唯一一次是在LIMIT 子句中。

SELECT
...
LIMIT ?, ?

MySQL 在这种情况下不接受带引号的字符串文字,也不接受字符串类型的参数。您必须使用整数。

请参阅Parametrized PDO query and `LIMIT` clause - not working 了解我对此的测试。那是关于PDO的问题,我没有测试mysqli,但我相信在这种情况下使用整数参数是服务器端MySQL的要求。所以它也应该适用于mysqli。

在所有其他情况下(AFAIK),MySQL 能够通过读取字符串中的前导数字并忽略任何后续字符来将字符串转换为整数。


@Dharman 在下面的评论中提到了 MySQL 在ORDER BY 中对整数的支持:

SELECT
...
ORDER BY ?

ORDER BY 中的整数表示按该位置的列排序,而不是按数字的常量值:

SELECT
...
ORDER BY 1 -- sorts by the 1st column

但包含该数字的等效字符串值的作用不同。它按字符串的常量值排序,也就是说每一行都是并列的,排序顺序是任意的。

SELECT
...
ORDER BY '1' -- sorts by a constant value, so all rows are tied

因此,这是查询参数的数据类型很重要的另一种情况。

另一方面,不推荐使用序数按ORDER BYGROUP BY 中该位置的列进行排序,我们不应依赖SQL 的这种用法。

【讨论】:

感谢您深入了解我的问题。我测试了 LIMIT 子句,但我无法用 php 7.0 重现它,MySQLi 客户端连接到 MariaDB 10.0。当我发出call_user_func_array([$rStmt, 'bind_param'], $aCall);($aCall 数组是 PHP 错误 #44139 的解决方法)时,LIMIT 子句接受字符串作为类型。 好吧,正如我在链接到的另一个答案中所写的那样,当使用 PDO 和模拟准备时,这可能只是一个问题。 我认为您对 PDO 的看法是正确的,因为 this 问题。 是的,LIMIT '10', '20' 不是有效的 MySQL 语法。 PDO 模拟准备的不稳定行为可能导致该语法。最好不要使用模拟准备。 @Code4R7,我更喜欢 PDO,只要禁用模拟准备。我不喜欢 mysqli 进行参数绑定的方式。 PDO 有更好的用法,你可以将一组值传递给execute()【参考方案2】:

看来我可以跳过这一步,默认情况下将每个参数作为字符串发送。

是的,没错。

是否有任何理由为每个参数指定“s”以外的其他数据类型?

极其罕见和模糊。到目前为止,我能够找到尽可能多的

bigint 值最好绑定为整数而不是字符串 有人报告说强制转换可能会导致错误的执行计划,但我无法在野外找到证据 您自己已经找到的二进制类型,但我会质疑将 BLOB 存储在数据库中的想法本身 那个奇数顺序的数字大小写mentioned by Dharman。

无论情况多么奇怪,我都建议保留类型绑定但避免类型嗅探,这没有好处,但可能会破坏您的数据库。

相反,只需明确类型但可选,就像我在mysqli helper function 中所做的那样:

function prepared_query($mysqli, $sql, $params, $types = "")

    $types = $types ?: str_repeat("s", count($params));
    $stmt = $mysqli->prepare($sql);
    $stmt->bind_param($types, ...$params);
    $stmt->execute();
    return $stmt;

当您不需要它们时(大部分时间),只需忽略类型:

$sql = "SELECT * FROM tmp_mysqli_helper_test WHERE id > ?";
$res = prepared_query($conn, $sql, [1])->get_result();

但每次你需要它时,它已经在这里并且明确了,你可以设置你想要的确切类型:

$sql = "SELECT * FROM tmp_mysqli_helper_test WHERE id > ?";
$res = prepared_query($conn, $sql, [1], "i")->get_result();

简单、干净、优雅!

【讨论】:

以上是关于mysqli_stmt::bind_param() - 为每个参数指定另一个数据类型而不是“s”的主要内容,如果未能解决你的问题,请参考以下文章

mysqli_stmt::bind_param():类型定义字符串中的元素数与绑定变量数不匹配

call_user_func_array() - 警告:mysqli_stmt::bind_param():变量数与准备语句中的参数数不匹配

绑定布尔值的 bind_param 问题

mysqli bind_param() 应该是一个参考,给定的值

使用准备好的语句的 PHP 注册脚本