【发布时间】:2020-01-21 09:22:18
【问题描述】:
我有一个表,按它们所属的数组存储数组元素,并且 他们在数组中的索引。它看起来很聪明,因为阵列是 预计是稀疏的,并且它们的元素会单独更新。 假设这是表格:
CREATE TABLE values (
pk TEXT,
i INTEGER,
value REAL,
PRIMARY KEY (pk, i)
);
pk | i | value
----+---+-------
A | 0 | 17.5
A | 1 | 32.7
A | 3 | 5.3
B | 1 | 13.5
B | 2 | 4.8
B | 4 | 89.1
现在我想将这些作为真正的数组,即 A 的 {17.5, 32.7, NULL, 53} 和 B 的 {NULL, 13.5, 4.8, NULL, 89.1}。
我原以为分组查询很容易实现
和适当的聚合函数。然而,事实证明那里
没有这样的函数可以通过索引将元素放入数组中(或
下标,就像 postgres 所说的那样)。如果
元素是连续的 - 我本来可以使用array_agg
ORDER BY i。但我想要结果中的空值
数组。
我最终得到的是这个怪物:
SELECT
pk,
ARRAY( SELECT
( SELECT value
FROM values innervals
WHERE innervals.pk = outervals.pk AND i = generate_series
)
FROM generate_series(0, MAX(i))
ORDER BY generate_series -- is this really necessary?
)
FROM values outervals
GROUP BY pk;
不得不SELECT … FROM values 两次是丑陋的,查询规划器似乎无法优化这一点。
有没有一种简单的方法可以在子查询中将分组的行作为关系引用,这样我就可以SELECT value FROM generate_series(0, MAX(i)) LEFT JOIN ????
通过定义custom aggregate function 来解决这个问题会更合适吗?
编辑:看起来我一直在寻找的可能是多参数unnest 和array_agg,虽然它不是特别优雅:
SELECT
pk,
ARRAY( SELECT val
FROM generate_series(0, MAX(i)) AS series (series_i)
LEFT OUTER JOIN
unnest( array_agg(value ORDER BY i),
array_agg(i ORDER BY i) ) AS arr (val, arr_i)
ON arr_i = series_i
ORDER BY series_i
)
FROM values
GROUP BY pk;
查询计划器甚至似乎 意识到它可以对已排序的 ,尽管我需要付出更多努力才能真正理解series_i 和 arr_i 进行排序合并 JOINEXPLAIN 输出。 编辑 2:实际上是 series_i 和 arr_i 之间的哈希连接,只有外部组聚合使用“排序”策略。
【问题讨论】:
-
我不认为它可以做得更好。请参阅
array_agg了解另一种方法。你不会摆脱generate_series。 -
@LaurenzAlbe 没有
generate_series:with t(a,b,c) as (values('a',1,1),('a',3,3),('a',4,4),('b',5,5)), cte as (select *, unnest(array_fill(null::int, array[c - coalesce(lag(b) over (partition by a order by b), -1) - 1]) || c) as d from t) select a, array_agg(d) as d from cte group by a; -
@LaurenzAlbe 请看我的编辑,
array_agg的用法是您的想法吗?我想知道是否可以只写一次array_agg和ORDER BY i,通过array_agg和unnest放置多个列。
标签: sql arrays postgresql aggregate-functions generate-series