PostgreSQL PL/pgSQL 来自值数组的随机值

问题描述 投票:0回答:6

如何声明一个具有两个或三个值的数组变量,并在执行过程中随机获取它们?

a := [1, 2, 5] -- sample sake
select random(a) -- returns random value

有什么建议从哪里开始吗?

arrays postgresql random plpgsql postgresql-9.2
6个回答
61
投票

试试这个:

select (array['Yes', 'No', 'Maybe'])[floor(random() * 3 + 1)];

20
投票
CREATE OR REPLACE FUNCTION random_pick()
  RETURNS int
  LANGUAGE sql VOLATILE PARALLEL SAFE AS
$func$
SELECT ('[0:2]={1,2,5}'::int[])[trunc(random() * 3)::int];
$func$;

random()
返回值
x
,其中
0.0 <= x < 1.0
。乘以
3
,然后用
trunc()
截断(比
floor()
稍快),以
 完全相等的机会
得到
0
1
2

Postgres 索引默认从 1 开始(根据 SQL 标准)。这将减少 1。我们可以每次加 1,或者声明数组索引以

0
开头。还快一点。参见:

PARALLEL SAFE
适用于 Postgres 9.6 或更高版本。参见:

如果您不想创建函数,可以使用简单的

SELECT
语句:

SELECT ('[0:2]={1,2,5}'::int[])[trunc(random() * 3)::int];

8
投票

Erwin Brandstetter 很好地回答了OP 的问题。然而,对于其他想要了解如何从更复杂的数组中随机选取元素的人(比如两个月前的我),我扩展了他的功能:

CREATE OR REPLACE FUNCTION random_pick( a anyarray, OUT x anyelement ) RETURNS anyelement AS $func$ BEGIN IF a = '{}' THEN x := NULL::TEXT; ELSE WHILE x IS NULL LOOP x := a[floor(array_lower(a, 1) + (random()*( array_upper(a, 1) - array_lower(a, 1)+1) ) )::int]; END LOOP; END IF; END $func$ LANGUAGE plpgsql VOLATILE RETURNS NULL ON NULL INPUT;

一些假设:

  • 这不仅适用于整数数组,还适用于任何类型的数组

  • 我们忽略NULL数据;仅当数组为空或插入 NULL 时才返回 NULL(其他非数组类型的值会产生错误)

  • 数组不需要像往常一样格式化 - 数组索引可以在任何地方开始和结束,可能有间隙等。

  • 这适用于一维数组

其他注意事项:

  • 如果没有第一个

    IF

    语句,空数组将导致无限循环

  • 如果没有循环,间隙和 NULL 将使函数返回 NULL

    如果您知道数组从零开始,则
  • 省略两个

    array_lower

    调用

  • 如果索引中有缺口,您将需要

    array_upper

     而不是 
    array_length
    ;没有间隙,它是相同的(不确定哪个更快,但它们应该没有太大不同)

    第二个
  • +1
  • 之后的

     
    array_lower
     用于以与任何其他值相同的概率获取数组中的最后一个值;否则需要 
    random()
     的输出恰好为 1,而这种情况永远不会发生

  • 这比 Erwin 的解决方案要慢得多,并且可能无法满足您的需求;在实践中,大多数人会从两者中混合出理想的鸡尾酒


8
投票
这是做同样事情的另一种方法

WITH arr AS ( SELECT '{1, 2, 5}'::INT[] a ) SELECT a[1 + floor((random() * array_length(a, 1)))::int] FROM arr;

您可以将数组更改为您想要的任何类型。


3
投票
CREATE OR REPLACE FUNCTION pick_random( members anyarray ) RETURNS anyelement AS $$ BEGIN RETURN members[trunc(random() * array_length(members, 1) + 1)]; END $$ LANGUAGE plpgsql VOLATILE;

CREATE OR REPLACE FUNCTION pick_random( members anyarray ) RETURNS anyelement AS $$ SELECT (array_agg(m1 order by random()))[1] FROM unnest(members) m1; $$ LANGUAGE SQL VOLATILE;
对于更大的数据集,请参阅:

  • http://blog.rhodiumtoad.org.uk/2009/03/08/selecting-random-rows-from-a-table/
  • http://www.depesz.com/2007/09/16/my-thoughts-on-getting-random-row/
  • https://blog.2ndquadrant.com/tablesample-and-other-methods-for-getting-random-tuples/
  • https://www.postgresql.org/docs/current/static/functions-math.html

0
投票
CREATE FUNCTION random_pick(p_items anyarray) RETURNS anyelement AS $$ SELECT unnest(p_items) ORDER BY RANDOM() LIMIT 1; $$ LANGUAGE SQL;
    
© www.soinside.com 2019 - 2024. All rights reserved.