我有一个名为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上应用group by,但不是每组返回1个单个结果,而是每组返回最多2个结果。还有条件得分> = 20,我希望结果按CID和SortKey排序。
如果我必须对上述数据运行查询,我希望得到以下结果:
C1的结果 - 注意:ROWID 1不被视为其分数< 20
C1, P2, 20, 2
C1, P3, 30, 3
C2的结果 - 注意:ROWID 5出现在ROWID 4之前,因为ROWID 5值较小 SORTKEY
C2, P5, 30, 2
C2, P4, 20, 3
结果为C3 - 注意:ROWID 6没有出现,因为它的得分低于20,所以这里只返回1条记录
C3, P7, 20, 2
简而言之,我想在一个分组旁边限制。我想要最简单的解决方案,并希望避免临时表。子查询很好。另请注意,我正在使用SQLite。
答案 0 :(得分:13)
这是一个相当便携的查询,可以执行您想要的操作:
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在LIMIT
使用的子查询中不支持IN
。
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'
子句中添加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
或其他内容。
答案 1 :(得分:4)
这实际上不是GROUP BY
问题(您没有聚合值)。这是一个每组最大的问题(我认为Stackoverflow实际上有一个greatest-n-per-group
标记。)
解决方案的确切细节取决于诸如每组是否有两次相同的排序键之类的问题。你可以从这样的事情开始:
SELECT * FROM table T1 WHERE Score > 20 AND
(SELECT COUNT(*) FROM table T2
WHERE T2.CID = T1.CID AND T2.SortKey <= T1.SortKey AND T2.RowID <> T1.RowID
AND T1.Score > 20) < 2;
ORDER BY CID, SortKey;
这样做只考虑分数大于20的那些行。然后,对于每个候选行,它计算同一表中具有分数&gt;的其他行的数量。 20但sortkeys小于或等于此行的排序键。如果该数字为0或1行,则此行有资格包含在结果中。
最后通过ORDER执行排序。
答案 2 :(得分:0)
在MySQL
:
SELECT l.*
FROM (
SELECT cid,
COALESCE(
(
SELECT id
FROM mytable li
WHERE li.cid = dlo.cid
AND li.score >= 20
ORDER BY
li.cid, li.id
LIMIT 1, 1
), CAST(0xFFFFFFFF AS DECIMAL)) AS mid
FROM (
SELECT DISTINCT cid
FROM mytable dl
) dlo
) lo, mytable l
WHERE l.cid >= lo.cid
AND l.cid <= lo.cid
AND l.id <= lo.mid
AND l.score >= 20
答案 3 :(得分:-2)
不应该像它一样简单 **
SELECT CID, PID, Score, SortKey
FROM T1
WHERE score >= 20
ORDER BY CID, Sortkey
**
或者我错过了什么?
答案 4 :(得分:-4)
我们可以在SQLite中使用LIMIT子句。例如:
select * from tableName where field1='something' limit 10;
这给出了第10行。