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

标签 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 上应用分组依据,但不是每组返回 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 演示:

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/

相关文章:

ios - 在 Xcode iOS SDK 中读取 SQLite

Django 不反射(reflect)对应用程序所做的更改

mysql - 优化 MySQL GROUP BY 查询

MySQL获取每个产品组具有最小列值的行

r - 使用 group_by(多个变量)时的 dplyr 问题

mysql - SQL:在不覆盖行的情况下加入后的 GROUP BY?

c# - MySql 和 SQlite 类实现接口(interface)

sql - 如何在 SQLite 中使用 select 语句更新表

python - 如何从 Python 中的 SQLite3 数据库查询构建字典列表?

group-by - clickhouse 下采样到 OHLC 时间条间隔