SQL查询 - 如何在组内应用限制

Raj*_*Raj 16 sqlite group-by

我有一个名为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
Run Code Online (Sandbox Code Playgroud)

我写了什么查询,以便它在CID上应用group by,但不是每组返回1个单个结果,而是每组返回最多2个结果.还有条件得分> = 20,我希望结果由CID和SortKey排序.

如果我必须对上面的数据运行我的查询,我会期望以下结果:

结果为C1 - 注意:ROWID 1不被视为其得分<20

C1, P2, 20, 2
C1, P3, 30, 3
Run Code Online (Sandbox Code Playgroud)

结果为C2 - 注意:ROWID 5出现在ROWID 4之前,因为ROWID 5的值较小SortKey

C2, P5, 30, 2
C2, P4, 20, 3
Run Code Online (Sandbox Code Playgroud)

C3的结果 - 注意:ROWID 6没有出现,因为它的分数小于20,所以这里只返回1条记录

C3, P7, 20, 2
Run Code Online (Sandbox Code Playgroud)

简而言之,我想在一个分组旁边限制.我想要最简单的解决方案,并希望避免临时表.子查询很好.另请注意,我正在使用SQLite.

Cra*_*ger 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";
Run Code Online (Sandbox Code Playgroud)

该查询使用具有排序和限制的相关子查询来生成ROWID应出现在最终结果中的s 列表.因为相关子查询是针对每一行执行的,无论它是否包含在结果中,它可能不如下面给出的窗口函数版本那样高效 - 但与该版本不同,它将适用于SQLite3,它不支持窗口功能.

此查询要求ROWID唯一(可用作主键).

我在PostgreSQL 9.2和SQLite3 3.7.11中测试了上述内容; 它在两者都很好.它不适用于MySQL 5.5或最新的5.6里程碑,因为MySQL不支持LIMIT使用的子查询IN.

SQLFiddle演示:

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)
Run Code Online (Sandbox Code Playgroud)

如果要筛选特定的CID,只需添加AND "CID" = 'C1'或者外部 WHERE子句.

这是一个密切相关的答案,更详细的例子:https://stackoverflow.com/a/13411138/398670


由于这最初被标记SQL(没有SQLite)...只是为了完整性,在PostgreSQL或其他具有SQL标准窗口函数支持的DB中我可能会这样做:

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";
Run Code Online (Sandbox Code Playgroud)

产生相同的结果.SQLFiddle,包括额外的C1行来演示限制过滤器实际工作:http://sqlfiddle.com/#!12/22829/1

如果您要过滤特定CID,只需添加AND "CID" = 'C1'或任何的内部 WHERE条款.


顺便说一句,您的测试数据不足,因为对于任何得分> 20的CID,它永远不会超过两行.


Lar*_*tig 5

这实际上不是GROUP BY问题(您没有聚合值)。这是一个最大的每组问题(我认为greatest-n-per-groupStackoverflow 上实际上有一个标签)。

解决方案的具体细节取决于问题,例如每个组是否两次使用相同的排序键。你可以从这样的事情开始:

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;
Run Code Online (Sandbox Code Playgroud)

这样做的作用是仅考虑那些分数高于 20 的行。然后,对于每个候选行,它会计算同一表中分数 > 20 但排序键小于或等于该行的排序键的其他行的数量。如果该数字为 0 或 1 行,则该行有资格包含在结果中。

最后 ORDER by 执行排序。


Qua*_*noi 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
Run Code Online (Sandbox Code Playgroud)