PostgreSQL:随机选择?

时间:2012-04-13 13:54:02

标签: postgresql random

我正在使用PHP进行PostgreSQL。

是否可以选择 FROM 中的特定数量的随机值WHERE 条件 < / p>

而不是

选择 FROM WHERE 条件

然后将它们转换为数组并使用array_rand()

(我不想这样使用,因为我将拥有数百万行并首先选择所有值,然后array_rand()可能需要花费很多时间。)


假设我有一张这样的表:

   name    |   items
-----------+------------
    Ben    | {dd,ab,aa}  
-----------+------------
   David   |  {dd,aa}  
-----------+------------
   Bryan   | {aa,ab,cd}
-----------+------------
    Glen   |    {cd}
-----------+------------
   Edward  |   {aa,cd}
-----------+------------
   Aaron   |  {dd,aa}
-----------+------------
  ..... (many many more)

更新

我需要选择匹配条件的一列(或基本上是10个随机行)中的10个随机值(在这种情况下为@> ARRAY[aa]),而不进行顺序表扫描或时间 - 耗时。

order by random()将花费大量时间,因为它必须处理每一行,所以我会选择更好的解决方案。

2 个答案:

答案 0 :(得分:5)

在PostgreSQL中,你可以order by random(),所以这应该是你想要的:

select name
from table
where items @>ARRAY['aa']
order by random()
limit 10;

答案 1 :(得分:1)

如果基表没有大量更新,此解决方案很好。其他维护成本可能超过收益。

如果您的条件始终为@> ARRAY[aa],则可以创建辅助查找表(基本上是物化视图)。

CREATE TABLE tbl_pick (rn serial, id int, PRIMARY KEY (rn, id);

INSERT INTO tbl_pick (id)
SELECT id FROM tbl
WHERE  items @> ARRAY[aa];

然后您可以应用与描述here类似的方法:

SELECT *
FROM  (
    SELECT 1 + floor(random() * <insert_count_plus_a_bit_here>)::integer AS rn
    FROM   generate_series(1, 20) g
    GROUP  BY 1                     -- trim duplicates
    ) r
JOIN   tbl_pick USING (rn)
JOIN   tbl USING (id)
LIMIT  10;                          -- trim surplus

这应该非常快,因为它只需要索引扫描,只能从表中读取~10行。

当然,您必须在(相关)INSERT / DELETE / UPDATE后更新tbl_picktbl。 可以向tbl_pick添加/删除少量更新(无更新),因为方法中有一些摆动空间。在进行一定数量的更新后,您需TRUNCATE并重新运行完整的INSERT。基本上重写您的物化视图。

UPDATE和DELETE可以使用tbl_pick的外键约束级联到ON UPDATE CASCADE ON DELETE CASCADE。并为新插入的行触发AFTER INSERT。这一切都取决于基表中的可能性。

并安排定期重写tbl_pick,最好在下班时间。

如果你的随机选择查询突然爆发,有一个“变量”指示tbl_pick是否脏(而不是fk约束和触发器)可能更便宜并且仅重新填充在运行查询之前的这种情况下的表(多次)。这在很大程度上取决于您的使用模式。这个“变量”可以是一行表,只允许UPDATE。在(相关)更新tbl后将其设置为TRUE,在刷新实体化视图后将其设置为FALSE。