3

我正在使用 PHP 开发 PostgreSQL。

是否可以WHERE条件的列中选择特定数量的随机值

代替

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()这将花费大量时间,因为它必须处理每一行,所以我将采用更好的解决方案。

4

2 回答 2

5

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

select name
from table
where items @>ARRAY['aa']
order by random()
limit 10;
于 2012-04-13T13:58:19.107 回答
1

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

如果您的条件是 always @> 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];

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

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 行。

当然,您必须tbl_pick在(相关)INSERT / DELETE / UPDATE 之后更新到tbl. 可以将少量更新添加/删除(无更新)到 tbl_pick,因为该方法中有一些回旋余地。经过一定数量的更新后,您将TRUNCATE重新运行完整的INSERT. 基本上重写你的物化视图。

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

并定期安排对 tbl_pick 的完全重写,最好是在下班时间。

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

于 2012-04-14T04:41:35.927 回答