Fre*_*abe 2 sql postgresql greatest-n-per-group postgresql-performance array-agg
我使用 PostgreSQL 14 来管理一个存储医生表更新的表:用户可以更新医生的名字、姓氏和/或年龄。更新操作未触及的字段具有值NULL。
这是涉及两名不同医务人员的四次编辑的示例。ID 为 3 的医生收到了三个更新:前两个正在更新字段age,第三个涉及first_name:
SELECT * FROM medic_edits;
Run Code Online (Sandbox Code Playgroud)
| ID | 医生ID | 名 | 姓 | 年龄 |
|---|---|---|---|---|
| 1 | 1 | 印地 | ||
| 2 | 3 | 59 | ||
| 3 | 3 | 63 | ||
| 4 | 3 | 鲍勃 |
我想合并此表,以便在结果表中每个医生有一行,提供累积编辑。这是我当前的查询及其产生的输出:
SELECT
medic_id,
(ARRAY_REMOVE(ARRAY_AGG(first_name ORDER BY id DESC), NULL))[1] AS first_name,
(ARRAY_REMOVE(ARRAY_AGG(last_name ORDER BY id DESC), NULL))[1] AS last_name,
(ARRAY_REMOVE(ARRAY_AGG(age ORDER BY id DESC), NULL))[1] AS last_name
FROM medic_edits
GROUP BY medic_id
;
Run Code Online (Sandbox Code Playgroud)
| 医生ID | 名 | 姓 | 姓 |
|---|---|---|---|
| 1 | 印地 | ||
| 3 | 鲍勃 | 63 |
这正是我所期望的输出,但我怀疑这个ARRAY_REMOVE/ARRAY_AGG逻辑有点浪费。我想知道是否有一种方法可以在这里使用分区来获得良好的利润,该FIRST_VALUE功能看起来非常相关。
是的,这很浪费。我希望这会更快:
SELECT DISTINCT ON (medic_id)
medic_id
, first_value(first_name) OVER (PARTITION BY medic_id ORDER BY CASE WHEN first_name IS NOT NULL THEN id END) AS first_name
, first_value(last_name) OVER (PARTITION BY medic_id ORDER BY CASE WHEN last_name IS NOT NULL THEN id END) AS last_name
, first_value(age) OVER (PARTITION BY medic_id ORDER BY CASE WHEN age IS NOT NULL THEN id END) AS age
FROM medic_edits;
Run Code Online (Sandbox Code Playgroud)
对于降序id值,请改用:
first_value(first_name) OVER (PARTITION BY medic_id ORDER BY CASE WHEN first_name IS NOT NULL THEN id END DESC NULLS LAST) AS first_name
Run Code Online (Sandbox Code Playgroud)
看:
但可能还有更快的方法。还取决于确切的表定义、基数和数据分布。
看:
关于DISTINCT ON:
可以在单个窗口中工作,SELECT因为DISTINCT或DISTINCT ON是在窗口函数之后应用的。看:
旁白:“年龄”将会迅速腐烂。它通常更适合存储生日。