准备好的语句,`WHERE .. IN(..)` 查询和排序 - 使用 MySQL

Posted

技术标签:

【中文标题】准备好的语句,`WHERE .. IN(..)` 查询和排序 - 使用 MySQL【英文标题】:A prepared statement, `WHERE .. IN(..)` query and sorting — with MySQL 【发布时间】:2011-04-11 19:37:06 【问题描述】:

假设我们有一个查询:

SELECT * FROM somewhere WHERE `id` IN(1,5,18,25) ORDER BY `name`;

以及要获取的 ID 数组:$ids = array(1,5,18,25)

对于准备好的语句,准备一个语句并多次调用它是adviced:

$stmt = $mysqli->prepare('SELECT * FROM somewhere WHERE `id`=?;');
foreach ($ids as $id)
    $stmt->bind_params('i', $id);
    $stmt->exec();
    

但现在我必须手动对结果进行排序。我有什么不错的选择吗?

【问题讨论】:

根据您提供的链接,我认为准备语句的原因是需要更新,它不支持在一个查询中进行多次更新。而你正在选择,所以你的第一个查询就足够了。 我确信在这种情况下准备好的陈述并不好。唯一不错的解决方案是“向上查询”并让这个 ID 数组排序在那里,而不是这里。 【参考方案1】:

我将添加一个缓慢而丑陋的解决方案,它仍然对任意数量的数组项使用准备好的语句:) 3 条语句在任何情况下都是通用的,并且可以在任何地方重复使用。

    CREATE TEMPORARY TABLE `ids`( `id` INT ); INSERT INTO `ids` VALUES(?); 这将插入您的 ID SELECT `id` FROM `ids` LEFT JOIN .... ; 使用其他表中的数据对ids 列表进行排序 SELECT `id` FROM `ids`; 全部选择回来

否则,您将不得不使用IN (?,?,?,.... 或手动对行进行排序。最好的办法是使用简单的 MySQL 查询,或者尝试获取已经按照您喜欢的方式排序的 ID 列表。

【讨论】:

【参考方案2】:

从我这里的答案https://***.com/posts/57934798/edit复制而来

使用命名占位符

$values = array(":val1"=>"value1", ":val2"=>"value2", ":val2"=>"$value3");
$statement = 'SELECT * FROM table WHERE `column` in(:'.implode(', :',array_keys($values)).') ORDER BY `column`';

使用 ??

$values = array("value1", "value2", "$value3");
$statement = 'SELECT * FROM table WHERE `column` in('.trim(str_repeat(', ?', count($values)), ', ').')  ORDER BY `column`';

【讨论】:

【参考方案3】:

遇到同样的问题,除了 7 年前@sled 的答案之外,这里有一种可能,无需进行 call_user_func_array(array($stmt, 'bind_param'), $ids); 步骤,但只调用一次 bind_params:

$ids = array(1,5,18,25);

// creates a string containing ?,?,? 
$bindClause = implode(',', array_fill(0, count($ids), '?'));
//create a string for the bind param just containing the right amount of iii
$bindString = str_repeat('i', count($ids));

$stmt = $mysqli->prepare('SELECT * FROM somewhere WHERE `id` IN (' . $bindClause . ') ORDER BY `name`;');

$stmt->bind_params($bindString, ...$ids);
$stmt->execute();

【讨论】:

非常聪明的代码。你的bind_params函数代码中$ids前面的三个点代表什么? 它被称为参数解包或splat运算符。 bind_params 函数接受可变数量的参数。 ...$ids 表示,数组在函数调用的第 n 个参数中解包。 bind_params 将抛出错误:调用未定义的方法 mysqli_stmt: :bind_params() 使用 bind_param 代替。 您能否针对单个查询中包含多个“where in”的情况提出建议?【参考方案4】:

我相信这是最简单的答案:

$ids = [1,2,3,4,5];
$pdos = $pdo->prepare("SELECT * FROM somwhere WHERE id IN (:"
        . implode(',:', array_keys($ids)) . ") ORDER BY id");

foreach ($ids as $k => $id) 
    $pdos->bindValue(":". $k, $id);


$pdos->execute();
$results = $pdos->fetchAll();

只要您的 Id 数组不包含键或带有非法字符的键,它就会起作用。

【讨论】:

【参考方案5】:

您是否考虑过使用 JOIN 和 WHERE 子句重写您的原始查询以获得所需的 IDS 以避免需要 WHERE IN 子句?我带着同样的问题来到这里,在查看了可能的解决方案后,我意识到 INNER JOIN 是我的解决方案。

【讨论】:

这是内部逻辑:应用需要通过 id 获取 N 个用户,由外部提供。很高兴你的情况不是那么具体:)【参考方案6】:

另一种方法是在结果对象上使用 php usort 函数,但这是“手动”。

看到这个: Sort Object in PHP

【讨论】:

【参考方案7】:

你可以这样做:

$ids = array(1,5,18,25);

// creates a string containing ?,?,? 
$clause = implode(',', array_fill(0, count($ids), '?'));


$stmt = $mysqli->prepare('SELECT * FROM somewhere WHERE `id` IN (' . $clause . ') ORDER BY `name`;');

call_user_func_array(array($stmt, 'bind_param'), $ids);
$stmt->execute();

// loop through results

使用这个你为每个 id 调用 bind_param 并且你已经通过 mysql 完成了排序。

【讨论】:

是的,但不好的是查询不能被重用,所以根本不需要准备它:) @o_O Tync:数据库访问次数越少越好。欢迎您遵守 PreparedStatement 约束,但如果您需要对同一个表运行 10/20/50/100/1000+ 个查询,它就无法扩展。 为这个语句创建一个视图不是更好吗,因为它不能被重用? @Ryan Schumacher:我错过了什么吗?我看不出视图对于传递过滤参数有什么好处。它只是一个存储的 SQL 语句... 这在 php 5.3 中是有问题的,原因是:ca.php.net/manual/en/mysqli-stmt.bind-param.php#96770【参考方案8】:

不,不推荐,如果您要使用ORDER BY 子句从数据库中获取某些记录。

【讨论】:

以上是关于准备好的语句,`WHERE .. IN(..)` 查询和排序 - 使用 MySQL的主要内容,如果未能解决你的问题,请参考以下文章

带有 IN() 条件的 WordPress 准备好的语句

MYSQLI Prepared statement update statement with where in array

设置准备好的语句参数 where NOT NULL java

准备好的语句中的动态 where 条件的 SQL 注入

使用 MySql、PHP 和 ADODB 在准备好的语句中参数化 IN 子句

带有命名占位符的 PDO 准备好的语句 IN 子句无法按预期工作 [重复]