我想要在PostgreSQL中随机选择行,我尝试了以下方法:
select * from table where random() < 0.01;
但是其他一些建议:
select * from table order by random() limit 1000;
我有一个很大的表,有5亿行,我希望它能很快。
哪种方法更好?有什么区别?选择随机行的最佳方法是什么?
根据您的要求(加上注释中的其他信息),
下面的查询不需要大表的顺序扫描,只需要索引扫描。
首先,获取主要查询的估算值:
SELECT count(*) AS ct -- optional
, min(id) AS min_id
, max(id) AS max_id
, max(id) - min(id) AS id_span
FROM big;
唯一可能昂贵的部分是count(*)
(用于大型桌子)。鉴于上述规格,您不需要它。估算就可以了,几乎可以免费获得):
SELECT reltuples AS ct FROM pg_class WHERE oid = 'schema_name.big'::regclass;
只要ct
不是 太大 小于id_span
,查询会优于其他方法。
WITH params AS (
SELECT 1 AS min_id -- minimum id <= current min id
, 5100000 AS id_span -- rounded up. (max_id - min_id + buffer)
)
SELECT *
FROM (
SELECT p.min_id + trunc(random() * p.id_span)::integer AS id
FROM params p
,generate_series(1, 1100) g -- 1000 + buffer
GROUP BY 1 -- trim duplicates
) r
JOIN big USING (id)
LIMIT 1000; -- trim surplus
在id
空间中生成随机数。您的差距很小,因此要检索的行数增加10%(足以轻松覆盖空白)。
每个都id
可以被偶然选择多次(尽管ID空间很大,这是不太可能的),因此请对生成的数字进行分组(或使用DISTINCT
)。
将id
s加入大表。有了适当的索引,这应该非常快。
最后,修剪id
未被骗子和缺口吃掉的剩余食物。每行都有 完全相等的机会 被选中。
您可以 简化 此查询。上面查询中的CTE仅用于教育目的:
SELECT *
FROM (
SELECT DISTINCT 1 + trunc(random() * 5100000)::integer AS id
FROM generate_series(1, 1100) g
) r
JOIN big USING (id)
LIMIT 1000;
特别是如果您不确定差距和估算值。
WITH RECURSIVE random_pick AS (
SELECT *
FROM (
SELECT 1 + trunc(random() * 5100000)::int AS id
FROM generate_series(1, 1030) -- 1000 + few percent - adapt to your needs
LIMIT 1030 -- hint for query planner
) r
JOIN big b USING (id) -- eliminate miss
UNION -- eliminate dupe
SELECT b.*
FROM (
SELECT 1 + trunc(random() * 5100000)::int AS id
FROM random_pick r -- plus 3 percent - adapt to your needs
LIMIT 999 -- less than 1000, hint for query planner
) r
JOIN big b USING (id) -- eliminate miss
)
SELECT *
FROM random_pick
LIMIT 1000; -- actual limit
我们可以在基本查询中使用 较小的盈余
。如果间隙太多,那么在第一次迭代中我们找不到足够的行,则rCTE会继续使用递归项进行迭代。我们仍然需要在ID空间中保持相对 较少的
间隙,否则在达到限制之前递归可能会枯竭-否则我们必须从足够大的缓冲区开始,这不利于优化性能。
重复项UNION
在rCTE中被消除。
LIMIT
一旦我们有足够的行,外部将使CTE停止。
该查询经过精心设计,可使用可用索引,生成实际上随机的行,并且直到我们达到限制后才停止(除非递归运行干了)。如果要重写它,这里有很多陷阱。
重复使用不同的参数:
CREATE OR REPLACE FUNCTION f_random_sample(_limit int = 1000, _gaps real = 1.03)
RETURNS SETOF big AS
$func$
DECLARE
_surplus int := _limit * _gaps;
_estimate int := ( -- get current estimate from system
SELECT c.reltuples * _gaps
FROM pg_class c
WHERE c.oid = 'big'::regclass);
BEGIN
RETURN QUERY
WITH RECURSIVE random_pick AS (
SELECT *
FROM (
SELECT 1 + trunc(random() * _estimate)::int
FROM generate_series(1, _surplus) g
LIMIT _surplus -- hint for query planner
) r (id)
JOIN big USING (id) -- eliminate misses
UNION -- eliminate dupes
SELECT *
FROM (
SELECT 1 + trunc(random() * _estimate)::int
FROM random_pick -- just to make it recursive
LIMIT _limit -- hint for query planner
) r (id)
JOIN big USING (id) -- eliminate misses
)
SELECT *
FROM random_pick
LIMIT _limit;
END
$func$ LANGUAGE plpgsql VOLATILE ROWS 1000;
称呼:
SELECT * FROM f_random_sample();
SELECT * FROM f_random_sample(500, 1.05);
您甚至可以使此泛型适用于任何表:将PK列的名称和表作为多态类型并使用EXECUTE
…但这超出了此问题的范围。
如果您的要求允许 相同的集合用于重复 调用(并且我们正在谈论重复调用),我将考虑一个 物化视图
。一次执行上述查询,然后将结果写入表中。用户以闪电般的速度获得准随机选择。在您选择的时间间隔或事件中刷新您的随机选择。
(n)`](https://www.postgresql.org/docs/current/sql-select.html#SQL-FROM)
n
百分比在哪里。手册:
的
BERNOULLI
和SYSTEM
采样方法的每一个接受单个参数,它是表格样本的分数,表示为 0到100之间的百分比
。此参数可以是任何real
值的表达式。
大胆强调我的。这是 非常快的 ,但结果 并非完全是随机的 。再次手册:
该
SYSTEM
方法比BERNOULLI
指定较小采样百分比时的方法要快得多,但是由于聚类的影响,它可能会返回表中的随机性较低的样本。
返回的行数可能相差很大。对于我们的示例,要获得 大约 1000行:
SELECT * FROM big TABLESAMPLE SYSTEM ((1000 * 100) / 5100000.0);
或 安装其他模块
tsm_system_rows 以获得确切的请求行数(如果有的话),并允许使用更方便的语法:
SELECT * FROM big TABLESAMPLE SYSTEM_ROWS(1000);
但这还不是完全随机的。
问题内容: 从Python目录中选择随机文件的最佳方法是什么? 编辑: 这是我在做什么: 这是特别糟糕,还是有一种更好的方法? 问题答案: 关于您编辑过的问题:首先,我假设您知道使用a的风险,以及从2.6开始不推荐使用并在3.0中删除的事实。 第二,我看不到这里存在任何比赛条件。您的对象基本上是不可变的(在缓存目录列表之后,再也不会读取它),因此并发读取它不会造成任何危害。 除此之外,我不明白为什
问题内容: 从大型mysql表中选择随机行的快速方法是什么? 我正在使用php,但是我对任何解决方案都感兴趣,即使它是另一种语言也是如此。 问题答案: 获取所有ID,从中随机选择一个ID,然后检索整行。 如果您知道ID是连续无孔的,则只需获取最大值并计算一个随机ID。 如果到处都有孔,但大多数是顺序值,并且您不关心随机偏斜,则获取最大值,计算一个id,然后选择ID等于或大于您所计算的ID的第一行。
问题内容: 如何在SQL(PostgreSQL)查询中选择20个随机行? 问题答案: SELECT column FROM table ORDER BY RANDOM() LIMIT 20
问题内容: 我有一个相当大的数据集和一个需要两个联接的查询,因此查询的效率对我来说非常重要。我需要根据联接的结果从数据库中检索3个满足条件的随机行。这里指出最明显的解决方案效率低下,因为 [这些解决方案]需要对所有表进行顺序扫描(因为需要计算与每一行关联的随机值-以便可以确定最小的行),即使对于中等大小的表也可能相当慢。 但是,那里的作者建议的方法(其中num_value是ID)对我不起作用,因为
问题内容: 我看到了许多与此主题有关的主题,但在了解如何做到这一点上一直没有成功。 例如,如果我有此表: 并且我只想显示“一个”类中的X个随机行,我该怎么做? 注意 :这是一张大桌子,所以我不想使用。 问题答案: 如您所知,大多数人推荐的解决方案无法扩展到大型表。 我在《SQL反模式:避免数据库编程的陷阱》一书中介绍了该解决方案和其他解决方案。 如果要使用PHP进行此操作,则可以执行以下操作(未测
问题 你想从一个序列中随机抽取若干元素,或者想生成几个随机数。 解决方案 random 模块有大量的函数用来产生随机数和随机选择元素。 比如,要想从一个序列中随机的抽取一个元素,可以使用 random.choice() : >>> import random >>> values = [1, 2, 3, 4, 5, 6] >>> random.choice(values) 2 >>> random