规范化1维数组的数组下标,使它们从1开始

时间:2012-08-17 18:52:13

标签: arrays performance postgresql

PostgreSQL可以从任何地方开始使用数组下标 考虑这个创建一个包含3个元素的数组的例子,下标为5到7:

SELECT ('[5:7]={1,2,3}'::int[]);

返回:

[5:7]={1,2,3}

例如,您可以使用

获得第一个元素
SELECT ('[5:7]={1,2,3}'::int[])[5];

我希望规范化 任何给定的1维数组从数组下标1开始
我能想到的最好:

SELECT ('[5:7]={1,2,3}'::int[])[array_lower('[5:7]={1,2,3}'::int[], 1):array_upper('[5:7]={1,2,3}'::int[], 1)]

或者,同样,更容易阅读:

WITH x(a) AS (
    SELECT '[5:7]={1,2,3}'::int[]
    )
SELECT a[array_lower(a, 1):array_upper(a, 1)]
FROM   x

你知道更简单/更快或更优雅的方式吗?


基准

为了测试性能,我掀起了这个快速基准 表有100k行,随机长度在1和11之间的简单整数数组:

CREATE TEMP TABLE t (a int[]);
INSERT INTO t -- now with actually varying subscripts
SELECT ('[' || g%10 || ':' || 2*(g%10) || ']={1'
            || repeat(','||g::text, g%10) || '}')::int[]
FROM   generate_series(1,100000) g;

EXPLAIN ANALYZE
SELECT 
       substring(a::text, '{.*$')::int[]       -- Total runtime: 949.304 ms
--     a[-2147483648:2147483647]               -- Total runtime: 283.877 ms
--     a[array_lower(a, 1):array_upper(a, 1)]  -- Total runtime: 311.545 ms
FROM   t

所以,是的,@ Daniel的想法稍快一点 @Kevin的文本转换也有效,但并没有获得多少积分。

还有其他想法吗?

3 个答案:

答案 0 :(得分:5)

有一个更简单的方法是丑陋的,但我认为技术上正确:从数组中提取最大可能的切片,而不是具有计算边界的精确切片。 它避免了两个函数调用。

示例:

select ('[5:7]={1,2,3}'::int[])[-2147483648:2147483647];

结果:

  int4   
---------
 {1,2,3}

答案 1 :(得分:3)

不确定这是否已经涵盖,但是:

SELECT array_agg(v) FROM unnest('[5:7]={1,2,3}'::int[]) AS a(v);

为了测试性能,我必须在测试表上添加id列。慢。

答案 2 :(得分:2)

最终,something more elegant popped up与Postgres 9.6 The manual:

  

可以省略 lower-bound 和/或 upper-bound 切片   符;缺失的边界由下限或上限替换   数组的下标。例如:

现在很简单:

SELECT ('[5:7]={1,2,3}'::int[])[:];

Daniel's solution with hard-coded max array subscripts大致相同的表现 - 这仍然是Postgres 9.5或更早版本的发展方式。