Postgres - 从多个有序行中选择非空白非空值

Posted

技术标签:

【中文标题】Postgres - 从多个有序行中选择非空白非空值【英文标题】:Postgres - select non-blank non-null values from multiple ordered rows 【发布时间】:2022-01-23 22:47:23 【问题描述】:

我需要根据优先级对来自多个来源的大量数据进行分组,但来自这些来源的数据质量不同 - 它们可能会丢失一些数据。 任务是将这些数据以尽可能完整的方式分组到一个单独的表中。

例如:

create table grouped_data (
  id serial primary key,
  type text,
  a text,
  b text,
  c int
);

create table raw_data (
  id serial primary key,
  type text,
  a text,
  b text,
  c int,
  priority int
);


insert into raw_data
(type, a,       b,         c,   priority)
values
('one', null,    '',        123, 1),
('one', 'foo',   '',        456, 2),
('one', 'bar',   'baz',     789, 3),
('two', null,    'two-b',   11,  3),
('two', '',      '',        33,  2),
('two', null,    'two-bbb', 22,  1);

现在我需要按type对记录进行分组,按priority排序,取第一个非空非空值,放入grouped_data。 在这种情况下,aone 的值将是 foo,因为包含该值的行的优先级高于具有 bar 的行。而c 应该是123,因为它的优先级最高。 two 组也是如此,对于每一列,我们采用非空、非空且具有最高优先级的数据,如果没有实际数据,则回退到 null

最后grouped_data预计有以下内容:

('one', 'foo', 'baz',     123),
('two', null,  'two-bbb', 22)

我已经尝试过分组、子选择、合并、交叉连接...唉,我对 PostgreSQL 的了解还不足以让它发挥作用。 我也想避免一件事 - 逐个浏览列,因为在现实世界中只有几十个列可供使用......

一个指向我一直用来解决这个问题的小提琴的链接:http://sqlfiddle.com/#!17/76699/1


更新:

谢谢大家! Oleksii Tambovtsev 的解决方案是最快的。在一组与真实案例非常相似的数据(200 万条记录,约 30 个字段)上,只需 20 秒即可生成完全相同的数据集,而这组数据之前以编程方式生成,耗时 20 多分钟。

eshirvana 的解决方案在 95 秒、Steve Kass 的 125 秒和 Stefanov.sm - 308 秒(仍然比编程方式快!)

谢谢大家:)

【问题讨论】:

【参考方案1】:

你应该试试这个:

SELECT
       type,
       (array_agg(a ORDER BY priority ASC) FILTER (WHERE a IS NOT NULL AND a != ''))[1] as a,
       (array_agg(b ORDER BY priority ASC) FILTER (WHERE b IS NOT NULL AND b != ''))[1] as b,
       (array_agg(c ORDER BY priority ASC) FILTER (WHERE c IS NOT NULL))[1] as c
FROM raw_data GROUP BY type ORDER BY type;

【讨论】:

如果需要合并数组类型列的值怎么办?假设现在还有一个列d 添加了类型“字符串数组”和默认值“''::character varying[]”。我将如何合并它?【参考方案2】:

你可以使用窗口函数first_value:

select distinct 
    type 
  , first_value(a) over (partition by type order by nullif(a,'') is null, priority) as a
  , first_value(b) over (partition by type order by nullif(b,'') is null, priority)  as b
  , first_value(c) over (partition by type order by priority) as c
from raw_data 

【讨论】:

【参考方案3】:
select distinct on (type) type, 
  first_value(a) over (partition by type order by (nullif(a, '') is null), priority) a, 
  first_value(b) over (partition by type order by (nullif(b, '') is null), priority) b, 
  first_value(c) over (partition by type order by (c is null), priority) c
from raw_data;

【讨论】:

【参考方案4】:

这也应该有效。

WITH types(type) AS (
  SELECT DISTINCT
    type
  FROM raw_data
)
SELECT
  type,
  (SELECT a FROM raw_data WHERE a > '' AND raw_data.type = types.type ORDER BY priority LIMIT 1) AS a,
  (SELECT b FROM raw_data WHERE b > '' AND raw_data.type = types.type ORDER BY priority LIMIT 1) AS b,
  (SELECT c FROM raw_data WHERE c IS NOT NULL AND raw_data.type = types.type ORDER BY priority LIMIT 1) AS c
FROM types
ORDER BY type;

【讨论】:

以上是关于Postgres - 从多个有序行中选择非空白非空值的主要内容,如果未能解决你的问题,请参考以下文章

使用“data.table”从重复行中选择非“NA”值——当有多个分组变量时

从 CSV 填充 postgres 表列时出现非空约束错误

从 Postgres 中的 JSONB 字段中选择不为空的值

从 Postgres 中的非结束日期范围列表中查找未覆盖的日期范围

SQL 查询以填补跨时间缺失的空白并获取最后一个非空值

如何阻止 Go gorm 在 Postgres 中对我的自引用外键强制非空约束