2010-09-20 80 views
2

想象我有以下SQL查询:可以使用MYSQLI和PHP将多个值绑定为单个参数吗?

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

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

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

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

回答

1

我可以绑定多个值作为一个 参数使用的mysqli和PHP的最简单的解决方案?

不,你不能。

对于您的情况,您应该以编程方式构建查询字符串。如果保证它总是三个值,那么可以将三个标记添加到SQL,然后通过循环遍历数组进行绑定。

+0

这意味着我想我将不得不使用call_user_func_array来实际调用bind_param方法 – 2010-09-20 16:20:28

+0

您可以循环访问数组。 – webbiedave 2010-09-20 16:23:17

+0

你能详细说明一下吗?怎么样?我只是一次绑定参数。 – 2010-09-20 17:13:58

0

implode

$idList = array('id1','id2','id3'); 
$query = "SELECT id,name FROM user WHERE id IN (?)"; 
$stmt = $db->prepare($query); 
$_param = is_array($idList) ? implode(',',$idList) : $idList; 
$stmt->bind_param(1, $_param); 
+0

试过了,没有运气 – 2010-09-20 16:19:21

+3

这不会正确地引用/转义您的列表,这会破坏参数化的目的! – 2010-09-20 16:30:46

4

你可以使用PHP写出占位符?使用str_repeat(),然后只是bind_param所有你的params在一个循环中。

只要注意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)"; 

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

+0

我可以使用循环与MYSQLI绑定参数吗?怎么样?在查看mysqli_stmt_bind_param时,我看到:“变量的数量和字符串类型的长度必须与语句中的参数匹配。” – 2010-09-20 16:37:54

+0

@Ed这实际上是一个有趣的问题,我没有注意到,因为我不使用mysqli。它看起来像'bind_param()'需要在所有这些调用中包含所有参数,所以循环将不起作用。看看手册,有些人想到了一些智能解决方案:http://www.php.net/manual/en/mysqli-stmt.bind-param.php#89097和它下面的建议'call_user_func_array ()' – Fanis 2010-09-20 22:20:25

1

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

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 

Side By Side,我们得到这张表(Fetch.php是“WHERE id =?“而准备的语句,而Fetch2.php使用”,其中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个元素,就利用这个准备d语句并重复执行。这是准备好的声明毕竟是为了设计的。当然,没有什么比单次往返数据库更好,但准备好的陈述方法可能具有可接受的性能。当然,在你自己的系统上进行基准测试。最有可能的是,上面的测试太琐碎了(没有任何连接或子查询......并且db与php脚本在同一个系统上......)

相关问题