提问者:小点点

如何在PostgreSQL 9.6和更低版本中洗牌数组?


以下自定义存储函数 -

CREATE OR REPLACE FUNCTION words_shuffle(in_array varchar[])
        RETURNS varchar[] AS
$func$
        SELECT array_agg(letters.x) FROM 
        (SELECT UNNEST(in_array) x ORDER BY RANDOM()) letters;
$func$ LANGUAGE sql STABLE;

正在PostgreSQL 9.5.3中洗牌字符数组:

words=> select words_shuffle(ARRAY['a','b','c','d','e','f']);
 words_shuffle 
---------------
 {c,d,b,a,e,f}
(1 row)

但是现在在我切换到PostgreSQL 9.6.2之后,该功能停止工作:

words=> select words_shuffle(ARRAY['a','b','c','d','e','f']);
 words_shuffle 
---------------
 {a,b,c,d,e,f}
(1 row)

可能是因为 ORDER BY RANDOM() 停止工作:

words=> select unnest(ARRAY['a','b','c','d','e','f']) order by random();
 unnest 
--------
 a
 b
 c
 d
 e
 f
(6 rows)

我正在寻找一个更好的混洗字符数组的方法,它可以在新的PostgreSQL 9.6中工作,也可以在9.5中工作。

我在开发中的文字游戏需要它,它使用Pl/PgSQL函数。

更新:

汤姆·莱恩的回复:

目标列表中SRF的扩展现在发生在ORDER BY之后。因此,ORDER BY正在对单个虚拟行进行排序,然后在那之后发生取消嵌套。看

https://git.postgresql.org/gitweb/?p=postgresql.git


共2个答案

匿名用户

通常,集合返回函数应放在 FROM 子句中:

select array_agg(u order by random())
from unnest(array['a','b','c','d','e','f']) u

   array_agg   
---------------
 {d,f,b,e,c,a}
(1 row) 

对于留档(着重部分由作者标明):

目前,返回集合的函数也可以在查询的选择列表中调用。对于查询自行生成的每一行,将调用函数返回集,并为函数结果集的每个元素生成一个输出行。但请注意,此功能已弃用,可能会在将来的版本中删除。

匿名用户

毫无疑问,这是一个变化,是由于优化器中的一些“改进”。鉴于文档中说这是可行的,这是令人沮丧的。

但是,我建议您不要依赖子查询:

SELECT array_agg(letters.x ORDER BY random())
FROM UNNEST(in_array) l(x);

这也适用于Postgres的订单版本。

文件中说:

或者,从排序的子查询提供输入值通常可以工作。例如:

SELECT xmlagg(x) FROM (SELECT x FROM test ORDER BY y DESC) AS tab;

但是这种语法在SQL标准中是不允许的,并且不能移植到其他数据库系统。

(我坦率地承认,“通常会工作”并不能保证。但在文档中包含不符合标准的代码样本确实会误导人。为什么不在聚合函数中使用ORDERBY子句显示正确的样本?)

https://www . PostgreSQL . org/docs/9.5/static/functions-aggregate . html