使用 ARRAY_AGG 获取列中的第一个非 NULL 值是否浪费?

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功能看起来非常相关。

Erw*_*ter 5

是的,这很浪费。我希望这会更快:

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因为DISTINCTDISTINCT ON是在窗口函数之后应用的。看:

旁白:“年龄”将会迅速腐烂。它通常更适合存储生日。