PostgreSQL 中的快速随机行:为啥 time (floor(random()*N)) + (select * from a where id = const) 比 select where i

Posted

技术标签:

【中文标题】PostgreSQL 中的快速随机行:为啥 time (floor(random()*N)) + (select * from a where id = const) 比 select where id = random 少 100 倍?【英文标题】:quick random row in PostgreSQL: why time (floor(random()*N)) + (select * from a where id = const) 100 times less then select where id = random?PostgreSQL 中的快速随机行:为什么 time (floor(random()*N)) + (select * from a where id = const) 比 select where id = random 少 100 倍? 【发布时间】:2020-01-30 14:41:57 【问题描述】:

我需要从 PostgreSQL 查询中快速选择行。我读了 Best way to select random rows PostgreSQL 。 quick random row selection in Postgres

到目前为止,我读过的最快的是:

CREATE EXTENSION IF NOT EXISTS tsm_system_rows;
SELECT myid  FROM mytable TABLESAMPLE SYSTEM_ROWS(1);

平均 2 毫秒。但正如 cmets 所述,它不是“完全随机的”。

我试过了

SELECT id FROM a OFFSET floor(random()*3000000) LIMIT 1;

15-200 毫秒。

最简单的想法是按 id 选择,因为我的 id 是连续的。但是

select floor(random ()*1000); 2ms
select * from a where id=233; 2ms (and again 2ms for other constants)

但是

SELECT * FROM a where id = floor(random ()*1000)::integer; 300ms!!!

为什么是 300 而不是 4?是否可以以某种方式重新排序、提示等以使 4 毫秒?

【问题讨论】:

【参考方案1】:

这是因为random() 被定义为易失性,所以 Postgres 再次为每一行评估它 - 有效地遍历所有行。

如果您想防止这种情况,请将其“隐藏”在(否则无用的)子选择后面:

SELECT * 
FROM a 
where id = (select trunc(random ()*1000)::integer);

【讨论】:

谢谢。有用。奇怪的是,它在没有::integer 的情况下变得很长。这是为什么?谢谢!【参考方案2】:

以下内容与@a-horse-with_no-name 回答后的 OP 问题严格相关:奇怪的是,它变得很长,没有 ::integer。这是为什么?

因为 ::integer 是 SQL 标准“select cast(number as integer)”的 Postgres 扩展 RANDOM() 返回的类型是双精度,并且在应用 TRUNC() 函数后仍然如此。显示的内容由您的系统决定。

结构 val::data_type 在其一般形式中表示将 val 转换为指定的 data_type(前提是存在有效的转换函数)。如果 val 本身是一个表达式,则格式变为 (val)::data_type。 下面一步一步地展示了 a-horse-with-no-name 的查询在做什么,并指出了该步骤的数据类型。 CTE 是严格的,因此每一步使用相同的值,因为每次使用 random() 会生成不同的值。

with gen  as (select random() n)
select  n,pg_typeof(n)                          --step1 get random value interval [0-1). 
     ,  n*1000, pg_typeof(n*1000)               -- get value into interval [0-999.9999...)  
     ,  trunc(n*1000), pg_typeof(trunc(n*1000)) -- reduce to interval [0,999.000)
     ,  trunc(n*1000)::integer, pg_typeof(trunc(n*1000)::integer) 
  from gen;                                     -- cast to integer interval [0-999)  

顺便说一句,上面并不严格需要 trunc() 函数,因为将 double 转换为整数会丢弃任何十进制数字。

我希望这可以帮助您了解正在发生的事情。

【讨论】:

谢谢。 “trunc() 函数不是严格需要的” - 不是,但括号是 ;-)

以上是关于PostgreSQL 中的快速随机行:为啥 time (floor(random()*N)) + (select * from a where id = const) 比 select where i的主要内容,如果未能解决你的问题,请参考以下文章

选择随机行PostgreSQL的最佳方法

从mysql中的大表中快速选择随机行

从mysql中的大表中快速选择随机行

sql 从PostgreSQL中选择随机行

从具有加权行概率的 PostgreSQL 表中选择随机行

通过 SQLAlchemy 获取随机行