你如何在PHP中编写一个准备好的MySQL语句,每次都会使用不同数量的参数?这种查询的一个例子是:
SELECT `age`, `name` FROM `people` WHERE id IN (12, 45, 65, 33)
IN
子句每次运行时都会有不同数量的id
。
我脑子里有两种可能的解决方案,但想看看是否有更好的方法。
可能的解决方案1 使语句接受100个变量,并使用保证不在表中的虚拟值填充其余变量;多次调用超过100个值。
可能的解决方案2 不要使用预先准备好的声明;构建并运行查询严格检查可能的注入攻击。
答案 0 :(得分:20)
我可以想到几个解决方案。
一种解决方案可能是创建临时表。为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);
答案 1 :(得分:10)
还有FIND_IN_SET
function,其第二个参数是逗号分隔值的字符串:
SELECT age, name FROM people WHERE FIND_IN_SET(id, '12,45,65,33')
答案 2 :(得分:4)
体面的sql包装器支持绑定到数组值。 即
$sql = "... WHERE id IN (?)";
$values = array(1, 2, 3, 4);
$result = $dbw -> prepare ($sql, $values) -> execute ();
答案 3 :(得分:2)
请从桌上取下#2。准备好的语句是您应该考虑保护自己免受SQL注入的唯一方法。
然而,您可以做的是生成一组动态的绑定变量。如果你需要7(或103),请不要100。
答案 4 :(得分:2)
我得到了答案:http://bugs.php.net/bug.php?id=43568
这是我解决问题的有效方法。现在我可以动态地使用尽可能多的参数。它们将与我在数组中的数字相同,或者在这种情况下,我将id从最后一个查询(找到所有id,其中email ='johndoe@gmail.com')传递给动态查询以获取所有关于每个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();
?>
答案 5 :(得分: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)
但毫无疑问,解决方案here是解决此问题的更一般方法。
答案 6 :(得分: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 append one array to another (not array_push or +),[PHP]: Error -> Too few arguments in sprintf();,http://no2.php.net/manual/en/mysqli-stmt.bind-param.php#89171,Pass by reference problem with PHP 5.3.1