【发布时间】:2020-07-24 09:00:03
【问题描述】:
核心要求:
通过submission_date 为指定的过滤条件type、plan、status 查找person_id 的最新条目。可能有更多这样的过滤器,但无论如何,按提交日期返回最新的逻辑是相同的。两种主要用途,一种用于在 UI 中进行分页查看,另一种用于生成报告。
WITH cte AS (
SELECT * FROM (
SELECT my_table.*, rank() OVER (PARTITION BY person_id ORDER BY submission_date DESC, last_updated DESC, id DESC) FROM my_table
) rank_filter
WHERE RANK=1 AND status in ('ACCEPTED','CORRECTED') AND type != 'CR' AND h_plan_id IN (10000, 20000)
)
SELECT
SELECT count(id) FROM cte group by id,
SELECT * FROM cte limit 10 offset 0;
group by 也不适用于 CTE。计数查询中所有 null 的联合可能适用于组合,但不确定。
我想将这两个组合成一个查询的主要原因是因为表很大并且窗口函数很昂贵。目前我使用单独的查询,它们基本上都运行相同的查询两次。
Postgres 版本 12。
\d my_table;
Table "public.my_table"
Column | Type | Collation | Nullable
--------------------------+-----------------------------+-----------+----------
id | bigint | | not null
h_plan_id | bigint | | not null
h_plan_submitter_id | bigint | |
last_updated | timestamp without time zone | |
date_created | timestamp without time zone | |
modified_by | character varying(255) | |
segment_number | integer | |
-- <bunch of other text columns>
submission_date | character varying(255) | |
person_id | character varying(255) | |
status | character varying(255) | |
file_id | bigint | | not null
Indexes:
"my_table_pkey" PRIMARY KEY, btree (id)
"my_table_file_idx" btree (file_id)
"my_table_hplansubmitter_idx" btree (h_plan_submitter_id)
"my_table_key_hash_idx" btree (key_hash)
"my_table_person_id_idx" btree (person_id)
"my_table_segment_number_idx" btree (segment_number)
Foreign-key constraints:
"fk38njesaryvhj7e3p4thqkq7pb" FOREIGN KEY (h_plan_id) REFERENCES health_plan(id) ON UPDATE CASCADE ON DELETE CASCADE
"fk6by9668sowmdob7433mi3rpsu" FOREIGN KEY (h_plan_submitter_id) REFERENCES h_plan_submitter(id) ON UPDATE CASCADE ON DELETE CASCADE
"fkb06gpo9ng6eujkhnes0eco7bj" FOREIGN KEY (file_id) REFERENCES x12file(id) ON UPDATE CASCADE ON DELETE CASCADE
附加信息
type 的可能值为 EN 和 CR,其中 EN 约占数据的 70%。
表格列宽select avg_width from pg_stats where tablename='mytable'; 共有 374 列,共 41 列,因此每列约 9 列。
这个想法是先向用户显示一些页面,然后他们可以通过其他参数进行过滤,例如file_name(每个文件通常有大约 5k 个条目),type(非常低的基数),member_add_id(高基数) )、plan_id(低基数,每 500k 到 100 万个条目将与一个计划 ID 相关联)。在所有情况下,业务要求是仅显示 submission_date 的一组特定计划 ID 的最新记录(对于每年完成的报告)。按 id 排序只是防御性编码,同一天可以有多个条目,即使有人编辑了倒数第二个条目因此触及了last_updated 时间戳,我们只想显示相同数据的最后一个条目。这可能永远不会发生,可以删除。
用户可以使用这些数据生成 csv 报告。
下面右连接查询的解释结果:
Nested Loop Left Join (cost=554076.32..554076.56 rows=10 width=17092) (actual time=4530.914..4530.922 rows=10 loops=1)
CTE cte
-> Unique (cost=519813.11..522319.10 rows=495358 width=1922) (actual time=2719.093..3523.029 rows=422638 loops=1)
-> Sort (cost=519813.11..521066.10 rows=501198 width=1922) (actual time=2719.091..3301.622 rows=423211 loops=1)
Sort Key: mytable.person_id, mytable.submission_date DESC NULLS LAST, mytable.last_updated DESC NULLS LAST, mytable.id DESC
Sort Method: external merge Disk: 152384kB
-> Seq Scan on mytable (cost=0.00..54367.63 rows=501198 width=1922) (actual time=293.953..468.554 rows=423211 loops=1)
Filter: (((status)::text = ANY ('{ACCEPTED,CORRECTED}'::text[])) AND (h_plan_id = ANY ('{1,2}'::bigint[])) AND ((type)::text <> 'CR'::text))
Rows Removed by Filter: 10158
-> Aggregate (cost=11145.56..11145.57 rows=1 width=8) (actual time=4142.116..4142.116 rows=1 loops=1)
-> CTE Scan on cte (cost=0.00..9907.16 rows=495358 width=0) (actual time=2719.095..4071.481 rows=422638 loops=1)
-> Limit (cost=20611.67..20611.69 rows=10 width=17084) (actual time=388.777..388.781 rows=10 loops=1)
-> Sort (cost=20611.67..21850.06 rows=495358 width=17084) (actual time=388.776..388.777 rows=10 loops=1)
Sort Key: cte_1.person_id
Sort Method: top-N heapsort Memory: 30kB
-> CTE Scan on cte cte_1 (cost=0.00..9907.16 rows=495358 width=17084) (actual time=0.013..128.314 rows=422638 loops=1)
Planning Time: 0.369 ms
JIT:
Functions: 9
Options: Inlining true, Optimization true, Expressions true, Deforming true
Timing: Generation 1.947 ms, Inlining 4.983 ms, Optimization 178.469 ms, Emission 110.261 ms, Total 295.660 ms
Execution Time: 4587.711 ms
【问题讨论】:
-
请(始终)声明您的 Postgres 版本,并更准确地定义“总数”。你的意思是
rank(),而不是row_number()? (因此每个person_id可能有多个行。)显示数据类型和约束的实际CREATE TABLE语句可能会有所帮助。 -
submission_date和last_updated未定义NOT NULL。这可能是您当前的ORDER BY的问题。这些真的应该是NOT NULL吗? -
我可以将它们设为不可为空的列。
-
您要返回哪些行,以哪种方式排序,有多少?您的问题中有很多零碎的信息,但对核心要求没有明确的定义。如果您希望每个
person_id的最新行只有在具有status in ('ACCEPTED','CORRECTED')的情况下,那么您当前的方法一开始就不正确。您必须按status过滤 为每个person_id提取最新行... -
我现在更新了核心要求。 “如果你想要每个 person_id 的最新行,只有在 ('ACCEPTED','CORRECTED') 中具有状态”是的,这是正确的。
标签: sql postgresql common-table-expression window-functions