对于固定的三列,这可能是一种可能的解决方案。
http://sqlfiddle.com/#!17/45dc7/137
免责声明:如果不同列中的值可能相同,这将不起作用。例如。一排 (42, NULL, "A42", NULL) 和一排 (23, "A42", NULL, NULL) 将导致不需要的结果。解决方法是将带有唯一分隔符的列标识符连接到字符串,并在操作后通过字符串拆分将其删除。
WITH test_table as (
SELECT *,
array_remove(ARRAY[column1,column2,column3], null) as arr, -- A
cardinality(array_remove(ARRAY[column1,column2,column3], null))as arr_len
FROM test_table )
SELECT
s.array_agg as aggregates, -- G
MAX(tt.column1) as column1,
MAX(tt.column2) as column2,
MAX(tt.column3) as column3
FROM (
SELECT array_agg(id) FROM -- E
(SELECT DISTINCT ON (t1.id)
t1.id, CASE WHEN t1.arr_len >= t2.arr_len THEN t1.arr ELSE t2.arr END as arr -- C
FROM
test_table as t1
JOIN -- B
test_table as t2
ON t1.arr @> t2.arr AND COALESCE(t2.column1, t2.column2, t2.column3) IS NOT NULL
OR t2.arr @> t1.arr AND COALESCE(t1.column1, t1.column2, t1.column3) IS NOT NULL
ORDER BY t1.id, GREATEST(t1.arr_len, t2.arr_len) DESC -- D
) s
GROUP BY arr
UNION
SELECT
ARRAY[id]
FROM test_table tt
WHERE COALESCE(tt.column1, tt.column2, tt.column3) IS NULL) s -- F
JOIN test_table tt ON tt.id = ANY (s.array_agg)
GROUP BY s.array_agg
A:聚合列值并删除 NULL 值。原因是我稍后会检查不适用于NULLs 的子集。这是您应该添加上述免责声明中提到的列标识符的地方。
B:CROSS JOIN 桌子对着自己。在这里,我正在检查一个列聚合是否是另一个列聚合的子集。只有NULL 值的行将被忽略(这是COALESCE 函数)
C:从第一个表或第二个表中获取长度最大的列数组。这取决于它的 id。
D:ORDER BY 是最长的数组,DISTINCT 确保每个 id 只给出最长的数组
E: 现在有许多 id 具有相同的列数组集。数组集用于聚合 id。这里将 id 放在一起。
F:添加所有 NULL 行。
G:最后一个JOIN 针对所有列。这些行是来自 (E) 的 id 聚合的一部分。之后,MAX 值按列分组。
编辑: Fiddle for PostgreSQL 9.3(array_length 代替 cardinality 函数)并添加了测试用例 (8, 'A2', 'A3', 'A8')
http://sqlfiddle.com/#!15/8800d/2