【问题标题】:PostgreSQL PL/pgSQL random value from array of values来自值数组的 PostgreSQL PL/pgSQL 随机值
【发布时间】:2012-12-27 06:25:27
【问题描述】:

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

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

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

【问题讨论】:

    标签: arrays postgresql random plpgsql postgresql-9.2


    【解决方案1】:
    CREATE FUNCTION random_pick()
      RETURNS int AS
    $func$
    DECLARE
       a int[] := '{[0:2]=1,2,5}'; -- sample sake
    BEGIN
       RETURN a[floor((random()*3))::int];
    END
    $func$ LANGUAGE plpgsql VOLATILE
    

    random() 返回一个值 x,其中 0.0 <= x < 1.0。乘以三和floor() 得到0、1 或2,机会均等。对于以1 开头的默认数组索引,这将是一对一的。为了提高效率,我将数组索引声明为以0 开头。

    The manual on these mathematical functions.

    【讨论】:

      【解决方案2】:

      Erwin Brandstetter answered the OP's question 足够好了。然而,对于其他想了解如何从更复杂的数组中随机选择元素的人(比如两个月前的我),我扩展了他的功能:

      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;没有间隙,是一样的(不确定哪个更快,但应该差别不大)

      • 第二个array_lower 之后的+1 用于以与任何其他相同的概率获取数组中的最后一个值;否则它需要random() 的输出正好为 1,这永远不会发生

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

      【讨论】:

      • 为什么不使用array_length?
      • 这会导致只包含NULL 值的数组出现无限循环。可能想做a := ARRAY(SELECT x FROM unnest(a) AS rel(x) WHERE x NOT NULL)之类的事情。
      【解决方案3】:
      CREATE FUNCTION random_pick(p_items anyarray)
      RETURNS anyelement AS
      $$
         SELECT unnest(p_items) ORDER BY RANDOM() LIMIT 1;
      $$ LANGUAGE SQL;
      

      【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

      • 这是最简单的答案,并且不使用索引的固定值,而是使用数组长度.. 以我的拙见,这可能是最正确的答案!感谢分享! :D
      【解决方案5】:

      试试这个:

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

      【讨论】:

      • 对我来说这是最好的答案,因为不需要任何功能
      • 如果数组中有 3 个以上的项目,这是否会起作用?另外,您是否知道如何获得 2 个随机项目而不是 1 个?谢谢!
      【解决方案6】:
      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;
      
      

      对于更大的数据集,请参阅:

      【讨论】:

      • 我绝对喜欢这个,因为它使用数组作为参数,然后使用 array_length 而不是硬编码的数字。我最近成为width_bucket的粉丝,所以我的版本有RETURN vals[width_bucket(random(), 0, 1, array_length(vals, 1))];,但其他的都是一样的。
      猜你喜欢
      • 1970-01-01
      • 2019-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-27
      • 1970-01-01
      • 2017-05-25
      相关资源
      最近更新 更多