我想在PostgreSQL中随机选择行,我尝试了这个:

select * from table where random() < 0.01;

但也有人建议:

select * from table order by random() limit 1000;

我有一个非常大的表,有5亿行,我希望它是快速的。

哪种方法更好?有什么不同?选择随机行最好的方法是什么?


当前回答

Erwin Brandstetter所概述的物化观点“可能的替代方案”的变体是可能的。

例如,您不希望在返回的随机化值中出现重复值。一个示例用例是生成只能使用一次的短代码。

包含你的(非随机的)值集的主表必须有一些表达式来决定哪些行是“被使用的”,哪些行不是——在这里我将保持简单,只创建一个布尔列,并使用名称。

假设这是输入表(可能会添加其他列,因为它们不会影响解决方案):

id_values  id  |   used
           ----+--------
           1   |   FALSE
           2   |   FALSE
           3   |   FALSE
           4   |   FALSE
           5   |   FALSE
           ...

根据需要填充ID_VALUES表。然后,正如Erwin所描述的,创建一个物化视图,将ID_VALUES表随机化一次:

CREATE MATERIALIZED VIEW id_values_randomized AS
  SELECT id
  FROM id_values
  ORDER BY random();

注意,物化视图不包含已使用的列,因为这很快就会过时。视图也不需要包含id_values表中的其他列。

为了获得(并“使用”)随机值,在id_values上使用update - return,通过连接从id_values_randomised中选择id_values,并应用所需的条件来只获得相关的可能性。例如:

UPDATE id_values
SET used = TRUE
WHERE id_values.id IN 
  (SELECT i.id
    FROM id_values_randomized r INNER JOIN id_values i ON i.id = r.id
    WHERE (NOT i.used)
    LIMIT 1)
RETURNING id;

根据需要更改LIMIT——如果一次需要多个随机值,请将LIMIT更改为n,其中n是所需值的数量。

With the proper indexes on id_values, I believe the UPDATE-RETURNING should execute very quickly with little load. It returns randomized values with one database round-trip. The criteria for "eligible" rows can be as complex as required. New rows can be added to the id_values table at any time, and they will become accessible to the application as soon as the materialized view is refreshed (which can likely be run at an off-peak time). Creation and refresh of the materialized view will be slow, but it only needs to be executed when new id's added to the id_values table need to be made available.

其他回答

这是一个对我有用的决定。我想这很容易理解和执行。

SELECT 
  field_1, 
  field_2, 
  field_2, 
  random() as ordering
FROM 
  big_table
WHERE 
  some_conditions
ORDER BY
  ordering 
LIMIT 1000;
select * from table order by random() limit 1000;

如果知道需要多少行,请检查tsm_system_rows。

tsm_system_rows

module provides the table sampling method SYSTEM_ROWS, which can be used in the TABLESAMPLE clause of a SELECT command. This table sampling method accepts a single integer argument that is the maximum number of rows to read. The resulting sample will always contain exactly that many rows, unless the table does not contain enough rows, in which case the whole table is selected. Like the built-in SYSTEM sampling method, SYSTEM_ROWS performs block-level sampling, so that the sample is not completely random but may be subject to clustering effects, especially if only a small number of rows are requested.

首先安装扩展

CREATE EXTENSION tsm_system_rows;

然后你的问题,

SELECT *
FROM table
TABLESAMPLE SYSTEM_ROWS(1000);

从PostgreSQL 9.5开始,有一个新的语法专门用于从表中获取随机元素:

SELECT * FROM mytable TABLESAMPLE SYSTEM (5);

这个例子将给出mytable中5%的元素。

有关文档的更多说明:http://www.postgresql.org/docs/current/static/sql-select.html

我知道我有点晚了,但我刚刚找到了这个叫做pg_sample的很棒的工具:

pg_sample -从较大的PostgreSQL数据库中提取一个小的样本数据集,同时保持引用完整性。

我尝试了一个350M行的数据库,它真的很快,不知道随机性。

./pg_sample --limit="small_table = *" --limit="large_table = 100000" -U postgres source_db | psql -U postgres target_db

添加一个名为r的列,类型为serial。指数r。

假设我们有20万行,我们将生成一个随机数n,其中0 < n <= 200000。

选择r > n的行,按ASC排序,选择最小的行。

代码:

select * from YOUR_TABLE 
where r > (
    select (
        select reltuples::bigint AS estimate
        from   pg_class
        where  oid = 'public.YOUR_TABLE'::regclass) * random()
    )
order by r asc limit(1);

代码是自解释的。中间的子查询用于快速估计来自https://stackoverflow.com/a/7945274/1271094的表行数。

在应用程序级别,如果n >为行数或需要选择多行,则需要再次执行该语句。