SQL — 从 GROUP BY 中取前 3 个值

SQL — take Top 3 values from GROUP BY

Fiddle: https://www.db-fiddle.com/f/9F7XPiGtuQAYXQ99HfNJGN/3

CREATE TABLE table_1 (
  `country` VARCHAR(2),
  `category` VARCHAR(2),
  `cnt` INT
);

INSERT INTO table_1
  (`country`, `category`, `cnt`)
VALUES
  ('US', 'AA', 20),
  ('US', 'BB', 15),
  ('US', 'CC', 25),
  ('US', 'DD', 30),
  ('FR', 'AA', 10),
  ('FR', 'BB', 5),
  ('FR', 'CC', 50),
  ('FR', 'DD', 60);

这是数据:


| country | category | cnt |
| ------- | -------- | --- |
| FR      | DD       | 60  |
| FR      | CC       | 50  |
| FR      | AA       | 10  |
| FR      | BB       | 5   |
| US      | DD       | 30  |
| US      | CC       | 25  |
| US      | AA       | 20  |
| US      | BB       | 15  |

我想获取每个国家/地区的前 3 行(前 3 个类别),按 cnt 列排序。例如,我想要:

| country | category | cnt |
| ------- | -------- | --- |
| FR      | DD       | 60  |
| FR      | CC       | 50  |
| FR      | AA       | 10  |
| US      | DD       | 30  |
| US      | CC       | 25  |
| US      | AA       | 20  |

table 尚未由 cnt 订购。我尝试做 LIMIT 3 或 TOP 3,但它并没有像我 want/gives 我整体排名前 3,但不是每个国家/地区。

您可以使用 row_number():

select t.*
from (select t.*, row_number() over (partition by country order by cnt desc) as seqnum
      from t
     ) t
where seqnum <= 3;

您可以使用 dense_rank()

得到您的结果
select 
  *
from 
(
  select 
    *, 
    dense_rank() over (partition by country order by cnt desc) as rn
  from yourTable
) subq
where rn <= 3

请使用以下查询,

select country, category, cnt from
(select country, category, cnt, row_number() over(partition by country order by cnt 
desc) as rnk from table_1) qry
where rnk between 1 and 3;

演示:

我认为这里可以使用另一个概念,很高兴知道它。

可以使用maxmax_by函数,并使用两个函数得到的附加参数n,参数指向return n 值。参见 docs

因此您的查询如下所示:

SELECT country, max(cnt,3) top3_cnt, max_by(category,cnt,3) top3_category
FROM table_1
GROUP BY country

-------
Results:
country  top3_cnt       top3_category
FR       [60, 50, 10]   [DD, CC, AA]
US       [30, 25, 20]   [DD, CC, AA]

顺便说一句,我喜欢使用 WITH 使用命名表创建测试数据。而不是真正创建表和插入数据。

因此对于此查询,您可以将其用于测试目的:

WITH table_1 (country,category,cnt) AS ( 
 SELECT * FROM (VALUES
  ('US', 'AA', 20),
  ('US', 'BB', 15),
  ('US', 'CC', 25),
  ('US', 'DD', 30),
  ('FR', 'AA', 10),
  ('FR', 'BB', 5),
  ('FR', 'CC', 50),
  ('FR', 'DD', 60) ))