【问题标题】:How to access array internal index with postgreSQL?如何使用 postgreSQL 访问数组内部索引?
【发布时间】:2012-09-03 11:15:07
【问题描述】:

这是我的(可能对你来说很常见)非优化解决方案:

内部函数未优化的 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() 函数提供对“行的内部计数器”的直接访问,但它与行有关,与数组无关,不幸的是性能更差。

【问题讨论】:

    标签: arrays postgresql indexing


    【解决方案1】:

    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 开头的下标。

    这两种解决方案仅适用于 一维 数组! (可以轻松扩展到多个维度。)

    功能:

    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, <b>array_length($1,1)</b>) ...

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

    性能

    我对一个包含 1000 个 int 的数组进行了快速测试,到目前为止所有查询都在此处显示。它们的性能都差不多(~ 3.5 毫秒)——除了子查询上的row_number()(~ 7.5 毫秒)——正如预期的那样,因为子查询。

    更新:Postgres 9.4+

    除非您使用非标准索引下标进行操作,否则请改用新的 WITH ORDINALITY

    【讨论】:

    • 谢谢!那么,generate_series()之后又有两个新的解决方案了……什么是禁食:row_number()还是generate_subscripts()?跨度>
    • @PeterKrauss 这取决于你的用例(我的意思是用你的数据测试它)。 generate_subscripts() 对我来说更具可读性。但是,在正常大小的数组上使用时,我不会期望有太大的不同。
    • @PeterKrauss:我添加了一个替代解决方案,以及快速性能测试的结果。
    • 好的,将explain analyse 时间与“SELECT 1, unnest($1)”时间进行比较,我有 subscripts=109%、series=111% 和 row_number 150%。您使用generate_subscripts() 的解决方案是最好的(!),而我使用generate_series() 的解决方案并没有那么错误。
    • 致读者:WITH ORDINALITY 是完美的!在 pg9.5 中,它也适用于 JSON 和 JSONB 数组! SELECT * FROM jsonb_array_elements( '[20,11,3,5]'::JSONB ) WITH ORDINALITY
    【解决方案2】:

    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;
    

    【讨论】:

    • 是否保证保单?
    • @Quassnoi:我没测试过,没有排序就不能保证
    • 是的!我正在测试,我认为始终保留数组顺序。非常感谢,这就是解决方案。
    • @PeterKrauss:我已批准您的编辑。请注意,您也可以回答自己的问题 (see this thread)
    • 对不起,我改变了我的“选择的答案”:我的百分比时间基准与 row_number (150%)、generate_subscripts (109%) 和 generate_series (111%) 表明 row_number 是最差的 (!) ,并且 generate_subscripts 越快,那么它就在我附近需要“内部数组索引”和优化。
    猜你喜欢
    • 1970-01-01
    • 2016-05-21
    • 1970-01-01
    • 2012-08-29
    • 2020-07-17
    • 1970-01-01
    • 2018-09-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多