Vér*_*ace 1 postgresql cte greatest-n-per-group
这个问题是关于MySQL 5.6这里的一个问题的PostgreSQL 版本。最初,这是两个 RDBMS 的一个问题,但有人建议我,鉴于两个系统的不同功能,我应该拆分问题 - 特别是我认为 CTE(WITH 子句)应该使查询更加优雅和可读!
假设我有一个肿瘤列表(这个数据是根据真实数据模拟的):
CREATE table illness (nature_of_illness VARCHAR(25), created_at DATETIME);
INSERT INTO illness VALUES ('Cervix', '2018-01-03 15:45:40');
INSERT INTO illness VALUES ('Cervix', '2018-01-03 15:45:40');
INSERT INTO illness VALUES ('Cervix', '2018-01-03 15:45:40');
INSERT INTO illness VALUES ('Cervix', '2018-01-03 15:45:40');
INSERT INTO illness VALUES ('Cervix', '2018-01-03 15:45:40');
INSERT INTO illness VALUES ('Lung', '2018-01-03 17:50:32');
INSERT INTO illness VALUES ('Lung', '2018-02-03 17:50:32');
INSERT INTO illness VALUES ('Lung', '2018-02-03 17:50:32');
INSERT INTO illness VALUES ('Lung', '2018-02-03 17:50:32');
INSERT INTO illness VALUES ('Cervix', '2018-02-03 17:50:32');
-- 2017, with 1 Cervix and Lung each for the month of Jan - tie!
INSERT INTO illness VALUES ('Cervix', '2017-01-03 15:45:40');
INSERT INTO illness VALUES ('Lung', '2017-01-03 17:50:32');
INSERT INTO illness VALUES ('Lung', '2017-02-03 17:50:32');
INSERT INTO illness VALUES ('Lung', '2017-02-03 17:50:32');
INSERT INTO illness VALUES ('Lung', '2017-02-03 17:50:32');
INSERT INTO illness VALUES ('Cervix', '2017-02-03 17:50:32');
Run Code Online (Sandbox Code Playgroud)
您想找出在给定月份中哪个特定肿瘤最常见 - 到目前为止一切顺利!
现在,您会注意到,对于 2017 年的第 1 个月,存在平局 - 因此随机选择一个并给出答案是没有意义的- 因此必须包括平局- 这使问题更具挑战性。
我有一个解决方案,但它非常复杂 - 我想知道我的解决方案是否最佳。PostgreSQL 小提琴来了!小提琴中的查询非常麻烦 - 我会看看使用 CTE。
我的第一个答案(适用于 PostgreSQL 和 MySQL)包含在小提琴中,但我不会在这里发布它,因为我相信它将被 PostgreSQL 的卓越功能所取代,它只是我对 MySQL 的答案的副本题!
SELECT tumour_count, illness
FROM (
SELECT count(*) AS tumour_count, illness
, rank() OVER (ORDER BY count(*) DESC) AS rnk
FROM illness
WHERE created_at >= '2017-01-01' -- given month: 2007-01
AND created_at < '2017-02-01' -- optimized for index lookup
GROUP BY illness
) sub
WHERE rnk = 1;
Run Code Online (Sandbox Code Playgroud)
应该有一个索引(created_at),或者甚至(created_at, illness)可能允许仅索引扫描。
子查询比 Postgres 中的 CTE 快一点。所以只在需要的地方使用 CTE ,或者在性能不重要的时候使用。
有关的:
根据评论中的要求:
SELECT to_char(mon, 'YYYY-MM') AS month, tumour_count, illness
FROM (
SELECT date_trunc('month', created_at) AS mon
, illness
, count(*) AS tumour_count
, rank() OVER (PARTITION BY date_trunc('month', created_at)
ORDER BY count(*) DESC) AS rnk
FROM illness
WHERE created_at >= '2017-01-01' -- period from 2007-01 to 2019-01
AND created_at < '2019-02-01'
GROUP BY 1, 2
) sub
WHERE rnk = 1
ORDER BY mon, illness;
Run Code Online (Sandbox Code Playgroud)
如果您有领先或悬空的部分月份,请小心,计数可能会产生误导。
这在功能上等同于ypercube 已经提供的。只是为了更短/更快一些简化。并在给定的时间段内添加过滤器。
随着从表中读取的行的份额不断增加,索引支持变得不那么重要 - 并且在超过大约 5% 的情况下根本没有用。(例外情况适用,例如仅索引扫描。)
通过聚合绑定对等点,您仍然可以每月拥有1 行。喜欢:
SELECT to_char(mon, 'YYYY-MM') AS month, tumour_count, string_agg(illness, ' | ')
FROM (
SELECT date_trunc('month', created_at) AS mon
, illness
, count(*) AS tumour_count
, rank() OVER (PARTITION BY date_trunc('month', created_at)
ORDER BY count(*) DESC) AS rnk
FROM illness
WHERE created_at >= '2017-01-01' -- period from 2007-01 to 2019-01
AND created_at < '2019-02-01'
GROUP BY 1, 2
) sub
WHERE rnk = 1
GROUP BY mon, tumour_count
ORDER BY mon;
Run Code Online (Sandbox Code Playgroud)
db<>在这里摆弄
| 归档时间: |
|
| 查看次数: |
638 次 |
| 最近记录: |