7

我知道使用

SELECT unnest(ARRAY[5,3,9]) as id

如果没有ORDER BY子句,则无法保证结果集的顺序。例如,我可以得到:

id
--
3
5
9

但是下面的请求呢:

SELECT
  unnest(ARRAY[5,3,9]) as id,
  unnest(ARRAY(select generate_series(1, array_length(ARRAY[5,3,9], 1)))) as idx
ORDER BY idx ASC

是否保证 2 个unnest()调用(具有相同长度)将并行展开,并且索引idx确实与数组中项目的位置相匹配?

我正在使用 PostgreSQL 9.3.3。

4

2 回答 2

8

是的,这是 Postgres 的一个特性,并行取消嵌套保证是同步的(只要所有数组都具有相同数量的元素)。
Postgres 9.4 为并行 unnest 添加了一个干净的解决方案:

但是,不能保证结果行的顺序。实际上,用一个简单的语句:

SELECT unnest(ARRAY[5,3,9]) AS id

结果行的顺序是“保证的”,但 Postgres 不断言任何东西。只要没有明确定义顺序,查询优化器就可以自由地对行进行排序。这可能会对更复杂的查询产生副作用。

如果您问题中的第二个查询是您真正想要的(向未嵌套的数组元素添加索引号),那么generate_subscripts()有更好的方法:

SELECT unnest(ARRAY[5,3,9]) AS id
     , generate_subscripts(ARRAY[5,3,9], 1) AS idx
ORDER  BY idx;

此相关答案中的详细信息:

您将对WITH ORDINALITYPostgres 9.4感兴趣:

然后你可以使用:

SELECT * FROM unnest(ARRAY[5,3,9]) WITH ORDINALITY tbl(id, idx);
于 2014-05-23T20:50:02.837 回答
2

简短回答:不,在接受输出可能随机排序idx的前提时,不会匹配数组位置。unnest()

Demo:由于当前的实现unnest实际上是按照元素的顺序输出行,所以我建议在它上面加一层来模拟随机顺序:

CREATE FUNCTION unnest_random(anyarray)  RETURNS setof anyelement
language sql as
$$ select unnest($1) order by random() $$;

然后检查您的查询的一些执行,unnest替换为unnest_random

SELECT
  unnest_random(ARRAY[5,3,9]) as id,
  unnest_random(ARRAY(select generate_series(1, array_length(ARRAY[5,3,9], 1)))) as idx
ORDER BY idx ASC

输出示例:

编号 | 编号
-+-----
  3 | 1
  9 | 2
  5 | 3

id=3关联idx=13在数组中的第二个位置。都是错的。

查询有什么问题:它假设第一个unnest将使用与第二个相同的排列unnest(数学意义上的排列:数组中的顺序和行的顺序之间的关系)来打乱元素。但是这个假设与订单输出一unnest开始就不可预测的前提相矛盾。

关于这个问题

是否保证 2 个 unnest() 调用(具有相同的长度)将并行展开

in select unnest(...) X1, unnest(...) X2, withX1X2是 typeSETOF something并且具有相同的行数,X1并且X2将在最终输出中配对,以便X1rowN的值将面对X2同一 row的值N。(它是一种列的 UNION,而不是笛卡尔积)。

但我不会将这种配对描述为并行展开,所以我不确定这就是你的意思。

无论如何,这种配对对解决问题没有帮助,因为它发生在未嵌套调用丢失数组位置之后。

另一种选择:在pgsql-sql 邮件列表中的这个线程中,建议使用此功能:

CREATE OR REPLACE FUNCTION unnest_with_ordinality(anyarray, OUT value
anyelement, OUT ordinality integer)
  RETURNS SETOF record AS
$$
SELECT $1[i], i FROM
    generate_series(array_lower($1,1),
                    array_upper($1,1)) i;
$$
LANGUAGE sql IMMUTABLE; 

基于此,我们可以按第二个输出列排序:

select * from unnest_with_ordinality(array[5,3,9]) order by 2;
价值 | 序数
--------+------------
     5 | 1
     3 | 2
     9 | 3

对于 postgres 9.4 及更高版本:WITH ORDINALITY可以遵循 SET RETURNING 函数调用的子句将以通用方式提供此功能。

于 2014-05-24T19:24:07.640 回答