23

我需要将一组值绑定到WHERE IN(?)子句。我怎样才能做到这一点?

这有效:

$mysqli = new mysqli("localhost", "root", "root", "db");
if(!$mysqli || $mysqli->connect_errno)
{
    return;
}
$query_str= "SELECT name FROM table WHERE city IN ('Nashville','Knoxville')";
$query_prepared = $mysqli->stmt_init();
if($query_prepared && $query_prepared->prepare($query_str))
{       
    $query_prepared->execute();

但这我无法使用这样的 bind_param :

$query_str= "SELECT name FROM table WHERE city IN (?)";
$query_prepared = $mysqli->stmt_init();
if($query_prepared && $query_prepared->prepare($query_str))
{       
    $cities= explode(",", $_GET['cities']);
    $str_get_cities=  "'".implode("','", $get_cities)."'"; // This equals 'Nashville','Knoxville'

    $query_prepared->bind_param("s", $cities);
    $query_prepared->execute();

我究竟做错了什么?

我也尝试过call_user_func_array,但似乎无法获得正确的语法。

4

7 回答 7

49

这项任务有点复杂但可行。我将从我的文章Mysqli Prepared statement with multiple values for IN 子句中获取解释:

  • 首先,我们需要创建一个带有与?数组中元素一样多的标记的字符串。为此,我们将使用str_repeat()非常方便的函数。
  • 然后必须将这个带有逗号分隔问号的字符串添加到查询中。虽然它是一个变量,但在这种情况下它是安全的,因为它只包含常量值
  • 那么这个查询必须像任何其他查询一样准备好
  • 然后我们需要创建一个字符串,其类型与 bind_param() 一起使用。请注意,通常没有理由为绑定变量使用不同的类型 - mysql 很乐意将它们全部作为字符串接受。有边缘情况,但极为罕见。对于日常使用,您始终可以保持简单并使用“s”表示所有内容。str_repeat()又来救援了。
  • 然后我们需要将我们的数组值绑定到语句。不幸的是,你不能只把它写成一个变量,像这样$stmt->bind_param("s", $array),只有标量变量被允许在bind_param(). 幸运的是,有一个参数解包运算符可以满足我们的需要——将一组值发送到一个函数中,就好像它是一组不同的变量一样!
  • 其余的和往常一样 - 执行查询,获取结果并获取您的数据!

所以正确的示例代码是

$array = ['Nashville','Knoxville']; // our array
$in    = str_repeat('?,', count($array) - 1) . '?'; // placeholders
$sql   = "SELECT name FROM table WHERE city IN ($in)"; // sql
$stmt  = $mysqli->prepare($sql); // prepare
$types = str_repeat('s', count($array)); //types
$stmt->bind_param($types, ...$array); // bind array at once
$stmt->execute();
$result = $stmt->get_result(); // get the mysqli result
$data = $result->fetch_all(MYSQLI_ASSOC); // fetch the data   

尽管此代码相当大,但它比迄今为止本主题中提供的任何其他合理解决方案都要小得多。

于 2019-10-12T15:28:57.393 回答
13

您不能将两个变量与一个绑定question mark

对于您绑定的每个变量,您都需要一个question mark

“bind_param”检查每个变量是否符合要求。之后,字符串值放在引号之间。

这行不通。

"SELECT name FROM table WHERE city IN (?)"; ( becomes too )
$q_prepared->bind_param("s", $cities);
"SELECT name FROM table WHERE city IN ('city1,city2,city3,city4')";

一定是。

"SELECT name FROM table WHERE city IN (?,?,?,?)"; ( becomes too )
$q_prepared->bind_param("ssss", $city1,$city2,$city3,$city4);
"SELECT name FROM table WHERE city IN ('city1','city2','city3','city4')";

$query_prepared->bind_param一一引用字符串参数。
并且变量的数量和字符串类型的长度必须与语句中的参数相匹配。

$query_str= "SELECT name FROM table WHERE city IN ('Nashville','Knoxville')";

会变成

$query_str= "SELECT name FROM table WHERE city IN (?,?)";

现在bind_param必须是

bind_param("ss",$arg1,$arg2)

有了这个

$query_str= "SELECT name FROM table WHERE city IN (?)";

bind_param

bind_param("s",$cities)

你得到

$query_str= "SELECT name FROM table WHERE city IN ('Nashville,Knoxville')";

这就是数组不起作用的原因。
这个事实的唯一解决方案是call_user_func_array

如果你初始化一个语句,下面是不必要的

$query_prepared = $mysqli->stmt_init();
if($query_prepared && $query_prepared->prepare($query_str)) {

这是对的

$query_prepared = $mysqli->stmt_init();
if($query_prepared->prepare($query_str)) {

如果您不想使用call_user_func_array
并且只有少量参数
,则可以使用以下代码进行操作。

[...]
$cities= explode(",", $_GET['cities']);
if (count($cities)>3) { echo "too many arguments"; }
else
{ 
$count = count($cities); 
$SetIn = "(";
  for($i = 0; $i < $count; ++$i) {    
      $code.='s';
      if ($i>0) {$SetIn.=",?";} else {$SetIn.="?";}
  }
$SetIn.=")";
$query_str= "SELECT name FROM table WHERE city IN ".$SetIn;
// with 2 arguments $query_str will look like
// SELECT name FROM table WHERE city IN (?,?)
$query_prepared = $mysqli->stmt_init();
if($query_prepared->prepare($query_str))
  {       
    if ($count==1) { $query_prepared->bind_param($code, $cities[0]);}
    if ($count==2) { $query_prepared->bind_param($code, $cities[0],$cities[1]);}
    if ($count==3) { $query_prepared->bind_param($code, $cities[0],$cities[1],$cities[2]);
    // with 2 arguments $query_prepared->bind_param() will look like
    // $query_prepared->bind_param("ss",$cities[0],$cities[1])      
  }    

    $query_prepared->execute();
  } 
 [...]
 }

我建议您尝试使用call_user_func_array到达。

寻找nick9v
mysqli-stmt.bind-param的解决方案

于 2013-06-21T05:34:35.073 回答
4

像这样使用 call_user_func_array :

$stmt = $mysqli->prepare("INSERT INTO t_file_result VALUES(?,?,?,?)");

$id = '1111';
$type = 2;
$result = 1;
$path = '/root';

$param = array('siis', &$id, &$type, &$result, &$path);
call_user_func_array(array($stmt, 'bind_param'), $param);

$stmt->execute();

printf("%d row inserted. \n", $stmt->effected_rows);
$stmt->close;
于 2016-05-09T09:33:47.957 回答
2

从 PHP 8.1 版开始,不再需要绑定。与自 5.0 版以来的 PDO 一样,您现在可以将参数作为数组直接传递给 execute 方法

$mysqli       = new mysqli("localhost", "root", "root", "db");
$params       = ['Nashville','Knoxville'];
$placeholders = str_repeat('?,', count($params) - 1) . '?'
$query        = "SELECT name FROM table WHERE city IN ($placeholders)";
$stmt         = $mysqli->prepare($query);

$stmt->execute($params);

另一个示例,如果您有一个关联数组,其键与列名匹配:

$mysqli       = new mysqli("localhost", "root", "root", "db");
$data         = ["bar" => 23, "baz" => "some data"];
$params       = array_values($data);
$placeholders = str_repeat('?,', count($params) - 1) . '?'
$columns      = implode("`,`", array_keys($data));
$query        = "INSERT INTO foo (`$columns`) VALUES ($placeholders)";
$stmt         = $mysqli->prepare($query);

$stmt->execute($params);

另外值得一提的是,该库现在默认在发生错误时抛出异常。在 8.1 版之前,情况并非如此。

于 2021-09-24T20:57:53.120 回答
0

我也遇到了麻烦,并且eval在发现大多数人都在使用之前就开始使用它call_user_func_array

$fields = array('model','title','price'); // fields in WHERE clause
$values = array( // type and value for each field
    array('s','ABCD-1001'),
    array('s','[CD] Test Title'),
    array('d','16.00')
);
$sql = "SELECT * FROM products_info WHERE "; // start of query
foreach ($fields as $current){ // build where clause from fields
    $sql .= '`' . $current . '` = ? AND ';
}
$sql = rtrim($sql,'AND '); // remove last AND 
$stmt = $db->prepare($sql);
$types = ''; $vals = '';
foreach ($values as $index => $current_val){ // build type string and parameters
    $types .= $current_val[0];
    $vals .= '$values[' . $index . '][1],';
}
$vals = rtrim($vals,','); // remove last comma
$sql_stmt = '$stmt->bind_param("' . $types . '",' . $vals . ');'; // put bind_param line together
eval($sql_stmt); // execute bind_param
$stmt->execute();
$stmt->bind_result($col1,$col2,$col3,$col4,$col5,$col6); // this could probably also be done dynamically in the same way
while ($stmt->fetch()){
    printf("%s %s %s %s %s %s\n", $col1,$col2,$col3,$col4,$col5,$col6);
}
于 2014-02-02T11:00:40.737 回答
0

The way I did it: prepare the query with all its separate question marks, as well as the type string.

$cities = array('Nashville','Knoxville');
$dibs = '';
$query = "SELECT name FROM table WHERE city IN (";
$marks = array();

foreach ($cities as $k => $city) {
    // i,s,b,d type based on the variables to bind.
    $dibs .= 's';
    array_push($marks, '?');
}

$query .= implode(',', $marks) .')';

Connect.

$mysql = new mysqli($host, $user, $pass, $dbname);
$statement =
    $mysql->prepare($query)
OR  die(sprintf(
        'Query error (%s) %s', $mysql->errno, $mysql->error
    ))
;

Then you use "..." token / ellipsis (documentation) in order to bind the array.

if ($statement) {
    $statement->bind_param($dibs, ...$cities);
    $statement->execute();

    $statement->close();
}
$mysql->close();

I know it kinda defeats the purpose of binding in order to escape (but at least it works good with a list of integers, i.e. IDs). If you see a way how to improve this approach, feel free to edit/comment.

于 2019-06-25T21:44:04.977 回答
-3

这是我在将表单输入命名为与 mysql 列名相同之后所做的。

$post_fields = array_keys($_POST);
$post_values = array_values($_POST);

$fields_type_i = array("age","age_share","gender_share");  // all mysql col names type int

$fields = "";           // user input fields
$values = "";           // user input vals
$placeholders = "";     // ?,?,?
$params_type = "";      // s=string i=integer

foreach ($post_fields as $field) {
    $fields .= "`".$field."`,"; 
}

for ($i=0;$i<count($post_fields);$i++) {      // bind i and s param types
    $placeholders .= "?,";
    if (in_array($post_fields[$i],$fields_type_i)) {        
        $params_type .= "i";    
    } else {                                            
        $params_type .= "s";
    }
    $values .= $post_values[$i];
}

或者

for ($i=0;$i<count($post_fields);$i++) {        // binding only s param type
    if (in_array($post_fields[$i],$fields_type_i)) {
        $placeholders .= $post_values[$i].",";  
    } else {
        $placeholders .= "?,";
        $params_type .= "s";
        $values .= $post_values[$i];
    }
}

$fields = rtrim($fields,",");  // removing last commas
$values = rtrim($values,",");
$placeholders = rtrim($placeholders,",");

$params_string = $params_type.','.$values;
$params_vals = explode(",",$params_string);  // array of vals

$params_refs = array();
foreach($params_vals as $key => $value) $params_refs[$key] = &$params_vals[$key];  // array of refs

$stmt = $mysqli -> prepare('INSERT INTO pets ('.$fields.') VALUES ('.$placeholders.')');

if ($stmt && call_user_func_array(array($stmt, 'bind_param'), $params_refs) && $stmt -> execute()) {
    echo 'Success';
} else {
    echo $stmt -> error;
}
于 2019-10-12T14:31:12.687 回答