我原本期望
unnest()
返回数组中索引 1 到 array_length()
的一行,但显然情况并非如此。考虑这个例子:
create table T (
id int,
list int[]
);
insert into T values
(1, array[null, 42]), -- filled from the start
(2, array[]::int[]); -- we'll fill this later as we get values
update T set list[2] = 42 where id = 2; -- we got a value for id 2 now
select x.* from T, unnest(T.list) with ordinality as x (val, idx) where id = 1;
select x.* from T, unnest(T.list) with ordinality as x (val, idx) where id = 2;
两个
select
的输出为:
val | idx
-----+-----
| 1
42 | 2
val | idx
-----+-----
42 | 1
这是不幸的,因为在我的例子中元素的位置很重要。第二个查询显示值 42 的索引为 1,这打破了我想要做的事情。
我实际上很高兴 PostgreSQL 不会盲目地用
NULL
填充未设置的数组索引,因为这可能会节省大量存储空间(第二个数组存储为 [2:2]={42}
而不是 {NULL,42}
)。但在这种情况下,很不方便。
解决这个问题的最佳方法是什么?
背景:我们需要存储数十亿个 float8 值。它们以 EAV 格式(实体-属性-值)到达,我们按实体将它们聚合到数组中,属性提供数组索引。这已经不再是合理的了,但是庞大的数据量很难以其他方式进行管理。
编辑:我不知道为什么 StackOverflow 不断将“稀疏数组”标签更改为“稀疏矩阵”。这与多维数组无关。
不确定为什么它是单独记录的而不是与数组函数一起记录,但您可以使用
generate_subscripts
函数来实现此目的。与 WITH ORDINALITY
不同,它不会产生排序,而是产生可用于索引数组的实际下标:
SELECT list[x.idx] AS val, x.*
FROM T, generate_subscripts(T.list, 1) WITH ORDINALITY AS x (idx, ord);
(在线演示)
但是,请注意 Postgres 数组 实际上并不稀疏 - 它们只是具有任意(下限和上限)边界。我不确定这样做的目的是什么 - 也许它与数组切片的表示方式有关,也许它是为了支持基于 0 的索引(尽管这会导致比值更混乱),但是无论哪种情况
[当]分配给尚未存在的元素时[,]先前存在的元素和新分配的元素之间的任何位置都将用空值填充。