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 查询中快速 select 行。我读了
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 毫秒。但正如评论中所述,它不是 "completely random".
我试过了
SELECT id FROM a OFFSET floor(random()*3000000) LIMIT 1;
15-200 毫秒
最简单的想法是 select 按 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 毫秒?
这是因为 random()
被定义为可变的,所以 Postgres 会再次对每一行进行评估 - 有效地遍历所有行。
如果你想避免这种情况,"hide" 它在一个(否则无用的)子选择后面:
SELECT *
FROM a
where id = (select trunc(random ()*1000)::integer);
以下内容严格属于 @a-horse-with_no-name 回答后的 OP 问题:奇怪的是,它变成了 long w/out ::integer。这是为什么?
因为 ::integer 是 SQL 标准的 Postgres 扩展 "select cast( number as integer)" RANDOM() 返回的类型是双精度的,并且在应用 TRUNC() 函数后仍然如此。显示的内容由您的系统决定。
在其一般形式中,结构 val::data_type 表示将 val 强制转换为指定的 data_type(前提是存在有效的强制转换函数)。如果 val 本身是一个表达式,则格式变为 (val)::data_type。
下面逐步显示无名马的查询正在做什么,并指示该步骤的数据类型。 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() 函数,因为将双精度转换为整数会丢弃任何小数位。
我希望这可以帮助您了解正在发生的事情。
我需要从 PostgreSQL 查询中快速 select 行。我读了 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 毫秒。但正如评论中所述,它不是 "completely random".
我试过了
SELECT id FROM a OFFSET floor(random()*3000000) LIMIT 1;
15-200 毫秒
最简单的想法是 select 按 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 毫秒?
这是因为 random()
被定义为可变的,所以 Postgres 会再次对每一行进行评估 - 有效地遍历所有行。
如果你想避免这种情况,"hide" 它在一个(否则无用的)子选择后面:
SELECT *
FROM a
where id = (select trunc(random ()*1000)::integer);
以下内容严格属于 @a-horse-with_no-name 回答后的 OP 问题:奇怪的是,它变成了 long w/out ::integer。这是为什么?
因为 ::integer 是 SQL 标准的 Postgres 扩展 "select cast( number as integer)" RANDOM() 返回的类型是双精度的,并且在应用 TRUNC() 函数后仍然如此。显示的内容由您的系统决定。
在其一般形式中,结构 val::data_type 表示将 val 强制转换为指定的 data_type(前提是存在有效的强制转换函数)。如果 val 本身是一个表达式,则格式变为 (val)::data_type。
下面逐步显示无名马的查询正在做什么,并指示该步骤的数据类型。 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() 函数,因为将双精度转换为整数会丢弃任何小数位。
我希望这可以帮助您了解正在发生的事情。