我有一个名为 t1 的表,其中包含以下字段:ROWID、CID、PID、Score、SortKey
它有以下数据:
1, C1, P1, 10, 1
2, C1, P2, 20, 2
3, C1, P3, 30, 3
4, C2, P4, 20, 3
5, C2, P5, 30, 2
6, C3, P6, 10, 1
7, C3, P7, 20, 2
我写了什么查询,以便它在 CID 上应用分组依据,但不是每组返回 1 个结果,而是每组最多返回 2 个结果。还有条件是分数 >= 20,我想要按 CID 和 SortKey 排序的结果。
如果我必须对上述数据运行查询,我希望得到以下结果:
C1 的结果 - 注意:ROWID 1 不被视为其分数 < 20
C1, P2, 20, 2
C1, P3, 30, 3
C2 的结果 - 注意:ROWID 5 出现在 ROWID 4 之前,因为 ROWID 5 的值较小 排序键
C2, P5, 30, 2
C2, P4, 20, 3
C3 的结果 - 注意:ROWID 6 没有出现,因为它的分数小于 20,所以这里只返回 1 条记录
C3, P7, 20, 2
简而言之,我想要一个 GROUP BY 内的限制。我想要最简单的解决方案并希望避免使用临时表。子查询很好。另请注意,我为此使用了 SQLite。
最佳答案
这里有一个相当可移植的查询来做你想做的事:
SELECT *
FROM table1 a
WHERE a."ROWID" IN (
SELECT b."ROWID"
FROM table1 b
WHERE b."Score" >= 20
AND b."ROWID" IS NOT NULL
AND a."CID" = b."CID"
ORDER BY b."CID", b."SortKey"
LIMIT 2
)
ORDER BY a."CID", a."SortKey";
该查询使用具有排序和限制的相关子查询来生成应出现在最终结果中的 ROWID
列表。因为相关子查询是针对每一行执行的,无论它是否包含在结果中,它可能不如下面给出的窗口函数版本那么高效——但与该版本不同的是,它可以在不支持窗口的 SQLite3 上运行功能。
此查询要求ROWID
是唯一的(可以用作主键)。
我在 PostgreSQL 9.2 和 SQLite3 3.7.11 中测试了以上内容;它在两者中都可以正常工作。它不适用于 MySQL 5.5 或最新的 5.6 里程碑,因为 MySQL 在与 IN
一起使用的子查询中不支持 LIMIT
。
SQLFiddle 演示:
PostgreSQL(工作正常):http://sqlfiddle.com/#!12/22829/3
SQLite3(工作正常,相同的查询文本,但由于明显的 JDBC 驱动程序限制而需要单值插入):http://sqlfiddle.com/#!7/9ecd8/1
MySQL 5.5(有两种失败;MySQL 不喜欢
a."ROWID"
引用,即使在ANSI
模式下也是如此,所以我不得不取消引用;然后失败并显示This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery
): http://sqlfiddle.com/#!2/e1f31/2
SQLite 演示显示它在 SQLite3 命令行上运行良好:http://pastebin.com/26n4NiUC
输出(PostgreSQL):
ROWID | CID | PID | Score | SortKey
-------+-----+-----+-------+---------
2 | C1 | P2 | 20 | 2
3 | C1 | P3 | 30 | 3
5 | C2 | P5 | 30 | 2
4 | C2 | P4 | 20 | 3
7 | C3 | P7 | 20 | 2
(5 rows)
如果您想过滤特定的 CID
,只需将 AND "CID"= 'C1'
或任何内容添加到 outer WHERE
子句。
这是一个密切相关的答案,其中包含更详细的示例:https://stackoverflow.com/a/13411138/398670
因为这最初只是标记为 SQL
(没有 SQLite)...只是为了完整性,在 PostgreSQL 或其他具有 SQL 标准窗口函数支持的数据库中,我可能会这样做:
SELECT "ROWID", "CID", "PID", "Score", "SortKey"
FROM (
SELECT *, row_number() OVER (PARTITION BY "CID" ORDER BY "SortKey") AS n
FROM table1
WHERE "Score" >= 20
) x
WHERE n < 3
ORDER BY "CID", "SortKey";
产生相同的结果。 SQLFiddle,包括额外的 C1
行以证明限制过滤器确实有效:http://sqlfiddle.com/#!12/22829/1
如果您想过滤特定的CID
,只需将AND "CID"= 'C1'
或任何内容添加到内部 WHERE
子句。
顺便说一句,你的测试数据是不够的,因为对于任何得分 > 20 的 CID,它永远不会超过两行。
关于sqlite - sql查询 - 如何在组内应用限制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/751399/