MySQL:如何使用每个WHERE过滤器的限制进行查询

时间:2018-02-01 21:08:53

标签: php mysql sql

我需要按ID过滤查询,但我需要限制每个ID的结果。我不知道如何在一个查询中执行此操作,我只能使用PHP循环每个ID,对每个迭代执行查询,然后将结果合并在一起:

$allResults = [];
$ids = [1, 2, 3, 4, 5];

foreach ($ids as $id) {

    $db->query("
        SELECT *
        FROM example_table
        WHERE example_id = $id
        ORDER BY rating DESC
        LIMIT 2
    ");
    $results = $db->getResults();

    $allResults = array_merge($allResults, $results);
}

上面做了我需要的,但是如何在一个查询而不是5个查询的循环中执行此操作?

我正在寻找类似的东西:

    SELECT *
    FROM example_table
    WHERE 
        example_id = 1 LIMIT 2 OR
        example_id = 2 LIMIT 2 OR
        example_id = 3 LIMIT 2 OR
        example_id = 4 LIMIT 2 OR
        example_id = 5 LIMIT 2
    ORDER BY rating DESC

显然,where子句中的限制会引发错误,但你可以看到我想要实现的目标。任何帮助表示赞赏。

4 个答案:

答案 0 :(得分:2)

正如@Strawberry提到的,如果没有可验证的示例和数据,很难给出正确答案,但这是查询可能看起来的样子,因为mysql还不支持组排名功能。

SELECT * FROM
(SELECT *, 
    @f_rank := IF(@f_rating = rating AND @f_id = id, @f_rank + 1, 1) AS rank, 
    @f_rating := rating,
    @f_id := id
FROM   example_table 
WHERE id IN (1, 2, 3, 4, 5)
ORDER  BY id, rating DESC) tmp_table
WHERE tmp_table.rank <= 2

您需要做的是将每个ID的所有评分从1到n排名。然后选择前两个ID。

答案 1 :(得分:2)

您可以使用以下UNION查询:

(SELECT * FROM example_table WHERE id = 1 ORDER BY rating DESC LIMIT 2)
UNION ALL
(SELECT * FROM example_table WHERE id = 2 ORDER BY rating DESC LIMIT 2)
UNION ALL
(SELECT * FROM example_table WHERE id = 3 ORDER BY rating DESC LIMIT 2)
UNION ALL
(SELECT * FROM example_table WHERE id = 4 ORDER BY rating DESC LIMIT 2)
UNION ALL
(SELECT * FROM example_table WHERE id = 5 ORDER BY rating DESC LIMIT 2)

以下是如何在PHP中创建它的一种方法:

$ids = [1, 2, 3, 4, 5];

$sqlArray = array_map(function($id){
    $id = (int)$id;
    return "(SELECT * FROM example_table WHERE id = $id ORDER BY rating DESC LIMIT 2)";
}, $ids);

$sql = implode("\nUNION ALL\n", $sqlArray);

演示:http://rextester.com/GNDR46232

您还应该考虑使用占位符准备查询并绑定ID。因为我不知道这里的$db类是PDO方法:

$ids = [1, 2, 3, 4, 5];

$sqlArray = array_map(function($id){
    return "(SELECT * FROM example_table WHERE id = ? ORDER BY rating DESC LIMIT 2)";
}, $ids);

$sql = implode("\nUNION ALL\n", $sqlArray);

$stmt = $db->prepare($sql);
$stmt->execute($ids);
$allResults = $stmt->fetchAll(PDO::FETCH_ASSOC);

答案 2 :(得分:0)

它比我想象的要复杂得多,但你应该能够很容易地用PHP编程地创建查询。您甚至无法对UNION进行直接限制,但是您可以有效地对select上的select进行选择,并选择最低级别的限制。这是否比进行5次单独查询更有效是未知的。

SELECT *
      FROM (SELECT *
            FROM example_table
            WHERE id = 1
            LIMIT 2) AS id1
      UNION
      SELECT *
      FROM (SELECT *
            FROM example_table
            WHERE id = 2
            LIMIT 2) AS id2
      UNION
      SELECT *
      FROM (SELECT *
            FROM example_table
            WHERE id = 3
            LIMIT 2) AS id3
      .... more UNIONS ....;

答案 3 :(得分:0)

(你有一个名为“id”的属性并不是唯一的,这有点令人困惑)

基于联合的方法都意味着在查询中对example_id的值进行硬编码,并且不能很好地扩展到大量的example_ids。

OTOH这只会在查看值的子集时进行相对有效的查询。

这是另一种方法......

SELECT *
FROM
(
SELECT id, max(rating) as scndrt
FROM example_table scnd
INNER JOIN (
   SELECT id, max(rating) maxrt
   FROM example_table
   GROUP BY id) AS first
 ON scnd.id=first.id
 WHERE scnd.rating<first.maxrt
 ) AS rts
 INNER JOIN example_table thrd
 ON rts.id=thrd.id
 AND thrd.rating IN (rts.maxrt, rts.scndrt)

这不会返回只有一条记录的ID,如果(例如)最高评级对于id出现两次,则会返回2条以上的记录。它还需要多次传递数据。

我认为也可以使用查询中的变量来解决原始问题,例如....

 SELECT @rank:=1+IF(id=@previd, @rank, 0) as ranking,
 @previd=id AS currentid,
 FROM (
   SELECT *
   FROM example_table
   ORDER BY id, rating DESC
 ) AS src
 HAVING ranking<3

我没有使用这样的变量进行测试,因此您可能需要使用cursor将上面的选择展开到一个过程中,并将匹配的行写入临时表。