如何使用postgreSQL访问数组内部索引?

时间:2012-09-03 11:15:07

标签: arrays postgresql indexing

这是我(可能通常适合你)非优化解决方案:

使用非优化内部函数的PG问题的解决方法:

CREATE FUNCTION unnest_with_idx(anyarray)
RETURNS TABLE(idx integer, val anyelement) AS
$$ 
   SELECT generate_series(1,array_upper($1,1)) as idx, unnest($1) as val;
$$ LANGUAGE SQL IMMUTABLE;

测试:

SELECT idx,val from unnest_with_idx(array[1,20,3,5]) as t;

但是,正如我所说,非优化。我不敢相信(!!)PostgreSQL没有数组的内部索引......?但在这种情况下,问题是如何直接访问这个GIN-like内部计数器的索引?

注1:上述解决方案和问题与“how do you create an index by each element of an array?”不同。也与“Can PostgreSQL index array columns?”不同,因为该函数用于隔离数组,而不是用于数组字段的表索引。


NOTE2(在答案之后编辑):“数组索引”(更常用的术语)或“数组下标”或“数组计数器”是我们可以在语义路径中使用的术语来引用“内部计数器”,累加器到下一个数组项。我看到 没有PostgreSQL命令提供对此计数器的直接访问。作为generate_series()函数,generate_subscripts()函数是序列生成器,并且性能(最好但是)接近相同。另一方面,row_number()函数提供了对“内部行计数器”的直接访问,但它是关于行的,不是关于数组,不幸的是性能更差。

2 个答案:

答案 0 :(得分:6)

PostgreSQL generate array subscripts提供专用功能:

WITH   x(a) AS ( VALUES ('{1,20,3,5}'::int[]) )
SELECT generate_subscripts(a, 1) AS idx
      ,unnest(a) AS val
FROM   x;

实际上它与@Frank的查询几乎相同,只是没有子查询 此外,它适用于不以1开头的下标。

这两个解决方案仅适用于 1维数组! (可以很容易地扩展到多个维度。)

功能:

CREATE OR REPLACE FUNCTION unnest_with_idx(anyarray) 
RETURNS TABLE(idx integer, val anyelement) LANGUAGE SQL IMMUTABLE AS
$func$
  SELECT generate_subscripts($1, 1), unnest($1);
$func$;

呼叫:

SELECT * FROM unnest_with_idx('{1,20,3,5}'::int[]);

还要考虑:

SELECT * FROM unnest_with_idx('[4:7]={1,20,3,5}'::int[]);

有关this related question中数组下标的更多信息。

如果你真的想要标准化的下标(从1开始),我会使用:

SELECT generate_series(1, array_length($1,1)) ...

这几乎就是您已经使用array_length()而不是array_upper()的查询 - 这会因非标准下标而失败。

性能

我对1000 int的数组进行了快速测试,目前为止所有查询都在这里。它们都执行大约相同的(~3.5 ms) - 除了子查询上的row_number()(~7,5 ms) - 正如预期的那样,因为子查询。

更新:Postgres 9.4 +

除非您使用非标准索引下标,否则请使用新的 WITH ORDINALITY

答案 1 :(得分:1)

row_number()有效:

SELECT 
    row_number() over(), 
    value
FROM (SELECT unnest(array[1,20,3,5])) a(value);

然后,优化的功能将是

CREATE OR REPLACE FUNCTION unnest_with_idx(anyarray) 
RETURNS table(idx integer, val anyelement) AS $$ 
  SELECT (row_number() over())::integer as idx, val
  FROM (SELECT unnest($1)) a(val);
$$ LANGUAGE SQL IMMUTABLE;