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的主要内容,如果未能解决你的问题,请参考以下文章