我需要按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子句中的限制会引发错误,但你可以看到我想要实现的目标。任何帮助表示赞赏。
答案 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将上面的选择展开到一个过程中,并将匹配的行写入临时表。