【问题标题】:Can I bind multiple values as a single parameter using MYSQLI and PHP?我可以使用 MYSQLI 和 PHP 将多个值绑定为单个参数吗?
【发布时间】:2011-04-14 18:36:52
【问题描述】:

假设我有以下 SQL 查询:

SELECT id,name FROM user WHERE id IN ('id1','id2','id3')

现在想象我需要由 PHP 提供的 id 数组。所以我有这样的事情:

$idList = array('id1','id2','id3');
$query = "SELECT id,name FROM user WHERE id IN (?)";
$stmt = $db->prepare($query);
$stmt->bind_param(/*Something*/);

我可以用什么替换 /*Something*/ 以获得与原始查询相同的结果?还是我需要在查询格式中输入 3 个问号?我不想这样做的唯一原因是因为问号的数量是可变的,所以我必须手动构建查询字符串。

【问题讨论】:

    标签: php sql mysql mysqli


    【解决方案1】:

    我可以将多个值绑定为一个吗 参数使用MYSQLI和PHP?

    不,你不能。

    根据您的情况,您应该以编程方式构建查询字符串。如果您保证它始终是三个值,您可以在 SQL 中添加三个标记,然后通过遍历数组进行绑定。

    【讨论】:

    • 这意味着我想我也必须使用 call_user_func_array 来实际调用 bind_param 方法
    • 你可以循环遍历数组。
    • 您能详细说明一下吗?如何?我只一次绑定过参数。
    【解决方案2】:

    您可以使用 PHP 使用 str_repeat() 写出占位符 ?,然后只需 bind_param 循环中的所有参数。

    请注意 str_repeat 将返回的尾随逗号。要么使用rtrim(),要么使用array_fill() 创建重复占位符数组,然后使用join() 创建占位符字符串。

    $arrPlaceholders = array_fill(0, count($idList), '?') ;
    $strPlaceholders = join(', ', $arrPlaceholders) ;
    

    那么你的查询可以是:

    $query = "SELECT id,name FROM user WHERE id IN ($strPlaceholders)";
    

    你可以在一个循环中绑定你的参数。

    【讨论】:

    • 我可以使用循环将参数与MYSQLI绑定吗?如何?在查看 mysqli_stmt_bind_param 时,我看到:“变量的数量和字符串类型的长度必须与语句中的参数匹配。”
    • @Ed 这实际上是一个我没有立即注意到的有趣问题,因为我不使用 mysqli。看起来bind_param() 需要在一次调用中包含所有参数,因此循环将不起作用。看了手册,有些人想到了一些聪明的解决方案:php.net/manual/en/mysqli-stmt.bind-param.php#89097 和它下面建议的call_user_func_array()
    【解决方案3】:

    这可能有点旧,但我自己也想知道。所以我做了一个基准测试。首先,我创建了一个简单的表:

    SELECT * from random LIMIT 10;
    +----+------------+
    | id | rand_stuff |
    +----+------------+
    |  1 | 1988585319 | 
    |  2 | 1926594853 | 
    |  3 |  820681972 | 
    |  4 |  950331574 | 
    |  5 |  540721998 | 
    |  6 | 1284256353 | 
    |  7 |   12804417 | 
    |  8 | 2130482967 | 
    |  9 | 2018786156 | 
    | 10 |  285818156 | 
    +----+------------+
    
    SELECT count(id) from random;
    +-----------+
    | count(id) |
    +-----------+
    |   3365586 | 
    +-----------+
    
    /var/lib/mysql/benchmark# ls -laFh
    total 101M
    drwx------ 2 mysql mysql 4.0K 2011-05-28 00:06 ./
    drwxr-xr-x 7 mysql mysql 4.0K 2011-05-27 23:53 ../
    -rw-rw---- 1 mysql mysql   65 2011-05-27 23:53 db.opt
    -rw-rw---- 1 mysql mysql 8.4K 2011-05-28 00:06 random.frm
    -rw-rw---- 1 mysql mysql  55M 2011-05-28 00:32 random.MYD
    -rw-rw---- 1 mysql mysql  47M 2011-05-28 00:32 random.MYI
    

    它是一个大小约为 100 MB 的简单结构。随机数是使用 php 的 mt_rand() 函数创建的。

    这里是“Fetch.php”:

    <?php
    
    $loops = $argv[1];
    
    $mysqli = new mysqli("localhost", "bench", "bench", "benchmark");
    if(mysqli_connect_errno()){
        printf("Connect Failed: %s\n", mysqli_connect_error());
        exit();
    }
    
    if($stmt = $mysqli->prepare("SELECT rand_stuff FROM random WHERE id = ?")){
        for($i=1; $i<$loops; $i++){
            $stmt->bind_param("i", $i) or die;
            $stmt->execute() or die;
            $stmt->bind_result($value) or die;
            $stmt->fetch();      
            echo "$i \t $value\n";
        }
        $stmt->close();
    }
    

    还有一些基准测试:

    $time php fetch.php 10 > /dev/null
    
    real    0m0.043s
    user    0m0.024s
    sys 0m0.012s
    
    $ time php fetch.php 100 > /dev/null
    
    real    0m0.057s
    user    0m0.044s
    sys 0m0.000s
    
    $ time php fetch.php 1000 > /dev/null
    
    real    0m0.166s
    user    0m0.080s
    sys 0m0.012s
    
    $ time php fetch.php 10000 > /dev/null
    
    real    0m1.083s
    user    0m0.412s
    sys 0m0.124s
    

    这里是 fetch2.php

    <?php
    
    $loops = $argv[1];
    
    $mysqli = new mysqli("localhost", "bench", "bench", "benchmark");
    if(mysqli_connect_errno()){
        printf("Connect Failed: %s\n", mysqli_connect_error());
        exit();
    }
    
    $array = array();
    
    for($i=1; $i<$loops; $i++){
        $array[] = $i;
    }
    
    $joined_array = join($array, ',');
    
    $results = $mysqli->query("SELECT id, rand_stuff FROM random WHERE id IN ($joined_array)");
    
    while($row = $results->fetch_row()){
        $val1 = $row[0];
        $val2 = $row[1];
        echo "$val1\t$val2\n";
    }
    

    这里是它的相关基准。

    $time php fetch2.php 10 > /dev/null
    
    real    0m0.037s
    user    0m0.028s
    sys 0m0.008s
    
    $time php fetch2.php 100 > /dev/null
    
    real    0m0.044s
    user    0m0.032s
    sys 0m0.008s
    
    $ time php fetch2.php 1000 > /dev/null
    
    real    0m0.050s
    user    0m0.036s
    sys 0m0.016s
    
    
    $ time php fetch2.php 10000 > /dev/null
    real    0m0.117s
    user    0m0.088s
    sys 0m0.024s
    

    并排,我们得到这个表(Fetch.php 是“WHERE id = ?”和准备好的语句,而 Fetch2.php 在一个哑查询上使用“WHERE x IN ()”语法):

    +--------+-----------+------------+
    |  Loop  | Fetch.php | Fetch2.php |
    +--------+-----------+------------+
    | 10     |  .043s    |  .037s     |
    | 100    |  .057s    |  .044s     |
    | 1000   |  .116s    |  .050s     |
    | 10000  | 1.083s    |  .117s     |
    +--------+-----------+------------+
    

    显然,“Fetch2.php”效率更高,但在这个基准测试中……在您进入 100 多个元素范围之前,它似乎并不重要。对准备好的语句进行迭代既简单又安全(根本没有 SQL 注入的机会),并且在 ~10 个元素范围内似乎并不慢。使用约 10 个元素重复测试有时可以让“Fetch.php”赢得基准测试。总的来说,Fetch2.php 当然是赢了,但他们肯定在这个范围内很接近。

    我倾向于说...如果您的元素少于 100 个,只需利用准备好的语句并重复执行它。毕竟,这就是准备好的语句的设计目的。当然,没有什么比单次往返数据库更好的了,但是准备好的语句方法可能具有可接受的性能。当然,在您自己的系统上进行基准测试。很可能,上面的测试太简单了(没有任何连接或子查询......并且数据库与 php 脚本在同一系统上......)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-02
      • 2013-07-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多