【问题标题】:Select random value for each row为每一行选择随机值
【发布时间】:2018-03-04 13:12:31
【问题描述】:

我正在尝试从另一个表的列中为我正在更新的表的每一行选择一个新的随机值。我得到了随机值,但是我不能让它为每一行改变。有任何想法吗?代码如下:

UPDATE srs1.courseedition
SET ta_id = teacherassistant.ta_id
FROM srs1.teacherassistant
WHERE (SELECT ta_id FROM srs1.teacherassistant ORDER BY RANDOM()
       LIMIT 1) = teacherassistant.ta_id

【问题讨论】:

  • 实际的表定义、基数和您的 Postgres 版本会让这变得更有洞察力。

标签: sql postgresql random


【解决方案1】:

我的猜测是 Postgres 正在优化子查询,因为它不依赖于外部查询。您是否考虑过使用子查询?

UPDATE srs1.courseedition
    SET ta_id = (SELECT ta.ta_id
                 FROM srs1.teacherassistant ta
                 ORDER BY RANDOM()
                 LIMIT 1
                );

我认为这不会解决问题(智能优化器,唉)。但是,如果您与外部查询相关联,那么它应该每次都运行。也许:

UPDATE srs1.courseedition ce
    SET ta_id = (SELECT ta.ta_id
                 FROM srs1.teacherassistant ta
                 WHERE ce.ta_id IS NULL  -- or something like that
                 ORDER BY RANDOM()
                 LIMIT 1
                );

您可以将WHERE 子句替换为更无意义的内容,例如WHERE COALESCE(ca.ta_id, '') IS NOT NULL

【讨论】:

  • 我测试了两个代码,第一个对所有代码都返回相同的值(如您所料),但第二个只返回 null。我错过了什么吗?
  • @EuanHollidge 。 . .尝试ce.ta_id <> ta.ta_id 或其他方式将子查询与外部查询相关联。
  • 第一个查询肯定会像您提到的那样失败,并使用相同的值更新所有行。第二个查询是一个加载的footgun:仅正确更新带有courseedition.ta_id IS NULL 的行,所有其他行都设置为NULL,因为相关子查询不返回任何行。无论哪种方式,最好不要开始为每一行运行一个新的子查询。还取决于“随机”的确切定义。
  • @ErwinBrandstetter 。 . .我将条件更改为应该始终运行的条件。我同意这种方法可能不是分配随机值的最佳方式。 . .这是 OP 的原始路径。
  • @GordonLinoff:但WHERE COALESCE(ca.ta_id, '') IS NULL 更糟糕:会将 NULL 分配给 所有 行,因为相关子查询从不返回任何内容。
【解决方案2】:

与为每一行运行相关子查询相比,以下解决方案应该快几个数量级N 随机排序整个表与 1 随机排序。结果同样是随机的,但是我们使用这种方法得到了一个完全均匀的分布,而像Gordon's solution 这样的独立随机选择可以(并且可能会)比其他行更频繁地分配一些行。有不同种类的“随机”。需要仔细定义“随机性”的实际要求。

假设courseedition 中的行数大于teacherassistant 中的行数。

更新courseedition中的所有行:

UPDATE srs1.courseedition c1
SET    ta_id = t.ta_id
FROM  (
   SELECT row_number() OVER (ORDER BY random()) - 1 AS rn  -- random order
        , count(*) OVER () As ct                           -- total count
        , ta_id
   FROM   srs1.teacherassistant           -- smaller table
   ) t
JOIN (
   SELECT row_number() OVER () - 1 AS rn  -- arbitrary order
        , courseedition_id                -- use actual PK of courseedition
   FROM   srs1.courseedition              -- bigger table
   ) c ON c.rn%t.ct = t.rn                -- rownumber of big modulo count of small table
WHERE  c.courseedition_id = c1.courseedition_id;

注意事项

将大表的随机行号以小表的计数与小表的行数取模匹配。

row_number() - 1 获取从 0 开始的索引。允许更优雅地使用模运算符%

one 表的随机排序就足够了。较小的桌子更便宜。第二个可以有任何顺序(任意更便宜)。无论哪种方式,加入后的分配都是随机的。只有在较大表的排序顺序中存在规则模式时,才会间接损害完美随机性。在这种不太可能的情况下,将ORDER BY random() 应用于更大 表以消除任何此类影响。

【讨论】:

    猜你喜欢
    • 2013-04-09
    • 2012-06-04
    • 1970-01-01
    • 1970-01-01
    • 2014-06-26
    • 1970-01-01
    • 1970-01-01
    • 2016-02-26
    相关资源
    最近更新 更多