给定一个这样的表my_data
:
id | name | surname | age
----+------+---------+------
1 | john | smith | NULL
1 | NULL | smith | 32
1 | NULL | NULL | NULL
1 | john | smith | NULL
1 | john | NULL | 32
CREATE TABLE my_data(id,name,surname,age)
AS ( VALUES
(1::int, 'john', 'smith' ,NULL::int),
(1, NULL, 'smith' ,32),
(1, NULL, NULL ,NULL),
(1, 'john', 'smith' ,NULL),
(1, 'john', NULL ,32)
);
对于相同id
的,各个列中的值(如果存在)始终相同,因此我如何“压缩”它们以获得:
id | name | surname | age
----+------+---------+------
1 | john | smith | 32
我的尝试
A cross join lateral
for each column 是我目前唯一的想法,但我怀疑它是否好:
select
distinct column1, c2.value, c3.value, c4.value
from my_data md
cross join lateral (select column2 from my_data where column1 = md.column1 and column2 is not null limit 1) as c2(value)
cross join lateral (select column3 from my_data where column1 = md.column1 and column3 is not null limit 1) as c3(value)
cross join lateral (select column4 from my_data where column1 = md.column1 and column4 is not null limit 1) as c4(value);
使用
percentile_disc
我认为这样的事情是最快的,
这里我们使用
percentile_disc
一个Ordered-Set Aggregate描述为“离散百分位数:返回其在排序中的位置等于或超过指定分数的第一个输入值”。所以,first_value
在被聚合的行之上。NULLS LAST
),则第一个值不会为 null,这就是这里最重要的(因为你说“对于相同的 id,各个列中的值(如果存在)总是相同的”)我认为,您也可以
first_value
在窗口函数中执行此操作,然后使用DISTINCT ON
.使用
mode
如果你想要非价值观的共识,我们也可以做到。我假设
percentile_disc
这是 OP 想要的。另一种选择是使用mode()
不同的Ordered-Set Aggregate Function。它被描述为“返回最频繁的输入值(如果有多个相同频率的结果,则任意选择第一个)”。看起来像,Evan Carroll 的有趣解决方案获得了 +1(甚至从未听说过
percentile_disc
!)。但是,我有另一种可能的解决方案,它具有通用性的优点(对于那些
RDBMS
有CTE
s 的 s - 即WITH
子句)。它不需要任何特殊的 PostgreSQL 特定功能 - 除了
LIMIT
- 在其他服务器中具有相应的关键字。(完全重写!)
我做了以下事情:
创建我的表和数据:
然后运行以下命令:
与(期望的)结果(对数据正确):
即使存在异常值,此解决方案也能提供正确的解决方案 -(请参阅此帖子之前的编辑以了解问题!) - 它确实取决于大多数答案是否正确。使用该
UPPER()
功能还将消除任何大写问题。当然,更好的解决方案是在数据到达 HDD 附近之前
NOT NULL
进行数据清理 -例如,放入声明将是一个好的开始!