17

我有一个名为 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

简而言之,我想要一个 GROUP BY 中的限制。我想要最简单的解决方案,并希望避免使用临时表。子查询很好。另请注意,我为此使用 SQLite。

4

5 回答 5

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应出现在最终结果中的 s 列表。因为相关子查询是针对每一行执行的,无论它是否包含在结果中,它可能不如下面给出的窗口函数版本高效 - 但与该版本不同的是,它可以在不支持窗口的 SQLite3 上工作功能。

此查询要求ROWID是唯一的(可以用作主键)。

我在 PostgreSQL 9.2 和 SQLite3 3.7.11 中测试了上述内容;它在两者中都可以正常工作。它不适用于 MySQL 5.5 或最新的 5.6 里程碑,因为 MySQL 不支持LIMITIN.

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


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

于 2012-11-16T06:00:30.743 回答
4

这实际上不是GROUP BY问题(您不是在汇总值)。这是每组最大 n 的问题(我认为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;

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

最后 ORDER by 执行您的排序。

于 2011-10-22T03:17:51.780 回答
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
于 2009-04-15T12:07:51.433 回答
-1

不应该是像**这样简单的事情吗?

SELECT CID, PID, Score, SortKey
FROM T1
WHERE score >= 20
ORDER BY CID, Sortkey

**

还是我错过了什么?

于 2009-04-15T12:19:14.213 回答
-4

我们可以在 SQLite 中使用 LIMIT 子句。例如 :

select * from tableName where field1='something' limit 10;

这给出了前 10 行。

于 2011-06-02T14:14:15.237 回答