быстрая случайная строка в PostgreSQL: почему время (floor (random () * N)) + (выбрать * из где id = const) в 100 раз меньше, чем выбрать id = random? - PullRequest
0 голосов
/ 30 января 2020

Мне нужно быстро выбрать строку из PostgreSQL запроса. Я прочитал Лучший способ выбрать случайные строки PostgreSQL. быстрый случайный выбор строки в Postgres

Самым быстрым из прочитанных мной является:

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

2 мс в среднем. Но, как отмечено в комментариях, это не «абсолютно случайно».

Я пробовал

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

15-200 мс.

Самая простая идея - выбрать с помощью Идентификатор как мои идентификаторы являются непрерывными. Но

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? Есть возможность как-то переупорядочить, подсказывает et c. сделать 4 мс?

Ответы [ 2 ]

2 голосов
/ 30 января 2020

Это связано с тем, что random() определяется как изменчивый, поэтому Postgres снова оценивает его для каждой строки, эффективно проходя все строки.

Если вы хотите предотвратить это, «спрячьте» его за (иначе бесполезным) подвыбором:

SELECT * 
FROM a 
where id = (select trunc(random ()*1000)::integer);
1 голос
/ 31 января 2020

Следующее относится строго к OP-вопросу после ответа @ a-horse-with_no-name: Странно, но оно становится длинным w / out :: integer. Это почему?

Поскольку :: целое число является расширением Postgres к стандартному SQL «селекту выбора (число как целое число)», тип, возвращаемый функцией RANDOM (), имеет двойную точность и остается таким же после TRUN C () функция применяется. То, что отображается, определяется вашей системой.

В общем виде структура 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)  

Кстати, функция trun c () строго не нужна в приведенном выше, как приведение двойное целое число отбрасывает любые десятичные цифры.

Надеюсь, это поможет вам понять, что происходит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...