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

时间:2009-04-15 12:01:51

标签: 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

我编写什么查询以便它在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。

5 个答案:

答案 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演示:

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或其他内容。


BTW,您的测试数据不足,因为对于任何分数为&gt;的CID,它永远不会超过两行。 20无论如何。

答案 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行。