25

你将如何在 PHP 中编写一个准备好的 MySQL 语句,它每次都需要不同数量的参数?此类查询的一个示例是:

SELECT `age`, `name` FROM `people` WHERE id IN (12, 45, 65, 33)

该子句每次运行时IN都会有不同数量的s。id

我有两种可能的解决方案,但想看看是否有更好的方法。

可能的解决方案 1使语句接受 100 个变量,并用保证不在表中的虚拟值填充其余变量;多次调用超过 100 个值。

可能的解决方案 2不要使用准备好的语句;为可能的注入攻击构建和运行查询检查。

4

7 回答 7

21

我可以想到几个解决方案。

一种解决方案可能是创建一个临时表。为 in 子句中的每个参数插入表中。然后对你的临时表做一个简单的连接。

另一种方法可能是做这样的事情。

$dbh=new PDO($dbConnect, $dbUser, $dbPass);
$parms=array(12, 45, 65, 33);
$parmcount=count($parms);   // = 4
$inclause=implode(',',array_fill(0,$parmcount,'?')); // = ?,?,?,?
$sql='SELECT age, name FROM people WHERE id IN (%s)';
$preparesql=sprintf($sql,$inclause);  // = example statement used in the question
$st=$dbh->prepare($preparesql);
$st->execute($parms);

我怀疑但没有证据表明第一个解决方案可能更适合较大的列表,而后者适用于较小的列表。


为了让@orrd 开心,这里有一个简洁的版本。

$dbh=new PDO($dbConnect, $dbUser, $dbPass);
$parms=array(12, 45, 65, 33);
$st=$dbh->prepare(sprintf('SELECT age, name FROM people WHERE id IN (%s)',
                          implode(',',array_fill(0,count($parms),'?'))));
$st->execute($parms);

于 2008-11-29T09:36:05.837 回答
10

还有一个FIND_IN_SET函数,其第二个参数是一串逗号分隔值:

SELECT age, name FROM people WHERE FIND_IN_SET(id, '12,45,65,33')
于 2012-06-10T09:39:36.797 回答
4

体面的 sql 包装器支持绑定到数组值。IE

$sql = "... WHERE id IN (?)";
$values = array(1, 2, 3, 4);
$result = $dbw -> prepare ($sql, $values) -> execute ();
于 2008-11-29T09:55:41.083 回答
2

请把#2 从桌子上拿下来。准备好的语句是您应该考虑保护自己免受 SQL 注入的唯一方法。

但是,您可以做的是生成一组动态的绑定变量。即,如果您需要 7 个(或 103 个),就不要赚 100 个。

于 2008-11-29T09:47:51.210 回答
2

我的答案来自:http
://bugs.php.net/bug.php?id=43568 这是我解决问题的有效方法。现在我可以根据需要动态使用任意数量的参数。它们将与我在数组中的数字相同,或者在这种情况下,我将最后一个查询(找到 email = 'johndoe@gmail.com' 的所有 id)中的 id 传递给动态查询以获取所有无论我最终需要多少,关于这些 id 的信息。

<?php $NumofIds = 2; //this is the number of ids i got from the last query
    $parameters=implode(',',array_fill(0,$NumofIds,'?')); 
    // = ?,? the same number of ?'s as ids we are looking for<br />
    $paramtype=implode('',array_fill(0,$NumofIds,'i')); // = ii<br/>
    //make the array to build the bind_param function<br/>
    $idAr[] = $paramtype; //'ii' or how ever many ?'s we have<br/>
    while($statement->fetch()){ //this is my last query i am getting the id out of<br/>
        $idAr[] = $id;  
    }

    //now this array looks like this array:<br/>
    //$idAr = array('ii', 128, 237);

    $query = "SELECT id,studentid,book_title,date FROM contracts WHERE studentid IN ($parameters)";
    $statement = $db->prepare($query);
    //build the bind_param function
    call_user_func_array (array($statement, "bind_param"), $idAr);
    //here is what we used to do before making it dynamic
    //statement->bind_param($paramtype,$v1,$v2);
    $statement->execute();
?>
于 2009-01-18T19:27:14.897 回答
1

如果您只在IN子句中使用整数值,那么没有什么可以反对在不使用 SQL 参数的情况下动态构建查询。

function convertToInt(&$value, $key)
{
    $value = intval($value);
}

$ids = array('12', '45', '65', '33');
array_walk($ids, 'convertToInt');
$sql = 'SELECT age, name FROM people WHERE id IN (' . implode(', ', $ids) . ')';
// $sql will contain  SELECT age, name FROM people WHERE id IN (12, 45, 65, 33)

但毫无疑问,这里的解决方案是解决这个问题的更一般的方法。

于 2008-11-29T10:02:04.207 回答
0

我今天有一个类似的问题,我发现了这个话题。查看答案并在谷歌周围搜索,我找到了一个很好的解决方案。

虽然,我的问题有点复杂。因为我也有固定的绑定值和动态的。

这就是解决方案。

$params = array()
$all_ids = $this->get_all_ids();

for($i = 0; $i <= sizeof($all_ids) - 1; $i++){
    array_push($params, $all_ids[$i]['id']);
}

$clause = implode(',', array_fill(0, count($params), '?')); // output ?, ?, ?
$total_i = implode('', array_fill(0, count($params), 'i')); // output iiii

$types = "ss" . $total_i; // will reproduce : ssiiii ..etc

// %% it's necessary because of sprintf function
$query = $db->prepare(sprintf("SELECT * 
                                FROM clients    
                                WHERE name LIKE CONCAT('%%', ?, '%%') 
                                AND IFNULL(description, '') LIKE CONCAT('%%', ?, '%%')
                                AND id IN (%s)", $clause));

$thearray = array($name, $description);
$merge    = array_merge($thearray, $params); // output: "John", "Cool guy!", 1, 2, 3, 4

// We need  to pass variables instead of values by reference
// So we need a function to that
call_user_func_array('mysqli_stmt_bind_param', array_merge (array($query, $types), $this->makeValuesReferenced($merge))); 

函数 makeValuesreferenced

public function makeValuesReferenced($arr){
    $refs = array();
    foreach($arr as $key => $value)
        $refs[$key] = &$arr[$key];
    return $refs;
}

获取此“诀窍”的链接:https ://bugs.php.net/bug.php?id=49946 ,PHP 将一个数组附加到另​​一个数组(不是 array_push 或 +)[PHP]:错误 -> 太少sprintf() 中的参数;, http://no2.php.net/manual/en/mysqli-stmt.bind-param.php#89171 , PHP 5.3.1 的引用传递问题

于 2014-01-17T10:35:14.460 回答