У меня есть таблица в postgres, содержащая пару миллионов строк. Я проверил в Интернете и нашел следующие
SELECT myid FROM mytable ORDER BY RANDOM() LIMIT 1;
Это работает, но очень медленно ... есть ли другой способ сделать этот запрос или прямой способ выбрать случайную строку, не читая всю таблицу? Кстати, myid - это целое число, но это может быть пустое поле.
postgresql
random
Хуан
источник
источник
Ответы:
Вы можете поэкспериментировать
OFFSET
, как вSELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
Это
N
количество строк вmytable
. Возможно, вам сначала потребуется выполнить a,SELECT COUNT(*)
чтобы определить значениеN
.Обновление (Энтони Хэтчкинс)
Вы должны использовать
floor
здесь:SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
Рассмотрим таблицу из 2-х строк;
random()*N
генерирует0 <= x < 2
и, например,SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
возвращает 0 строк из-за неявного округления до ближайшего int.источник
SELECT COUNT(*)
?, то есть использовать не все значения в таблице, а только их часть?EXPLAIN SELECT ...
с разными значениями N дает одинаковую стоимость для запроса, тогда, я думаю, лучше выбрать максимальное значение N.PostgreSQL 9.5 представил новый подход для более быстрого выбора образцов: TABLESAMPLE
Синтаксис
SELECT * FROM my_table TABLESAMPLE BERNOULLI(percentage); SELECT * FROM my_table TABLESAMPLE SYSTEM(percentage);
Это не оптимальное решение, если вы хотите выбрать только одну строку, потому что вам нужно знать КОЛИЧЕСТВО таблицы, чтобы рассчитать точный процент.
Чтобы избежать медленного COUNT и использовать быстрый TABLESAMPLE для таблиц от 1 до миллиардов строк, вы можете:
SELECT * FROM my_table TABLESAMPLE SYSTEM(0.000001) LIMIT 1; -- if you got no result: SELECT * FROM my_table TABLESAMPLE SYSTEM(0.00001) LIMIT 1; -- if you got no result: SELECT * FROM my_table TABLESAMPLE SYSTEM(0.0001) LIMIT 1; -- if you got no result: SELECT * FROM my_table TABLESAMPLE SYSTEM(0.001) LIMIT 1; ...
Это может выглядеть не так элегантно, но, вероятно, быстрее, чем любой другой ответ.
Чтобы решить, хотите ли вы использовать BERNULLI или SYSTEM, прочтите о различиях на http://blog.2ndquadrant.com/tablesample-in-postgresql-9-5-2/
источник
SELECT * FROM my_table TABLESAMPLE SYSTEM(SELECT 1/COUNT(*) FROM my_table) LIMIT 1;
?SELECT reltuples FROM pg_class WHERE relname = 'my_table'
для подсчета количества.Я попробовал это с помощью подзапроса, и он работал нормально. Смещение, по крайней мере, в Postgresql v8.4.4 работает нормально.
select * from mytable offset random() * (select count(*) from mytable) limit 1 ;
источник
Вам необходимо использовать
floor
:SELECT myid FROM mytable OFFSET floor(random()*N) LIMIT 1;
источник
random()*N
генерирует 0 <= x <2 и, например,SELECT myid FROM mytable OFFSET 1.7 LIMIT 1;
возвращает 0 строк из-за неявного округления до ближайшего int.order by random()
, примерно как3*O(N) < O(NlogN)
- реальные цифры будут немного отличаться из-за индексов.WHERE myid NOT IN (1st-myid)
иWHERE myid NOT IN (1st-myid, 2nd-myid)
они не будут работать, поскольку решение принимается с помощью OFFSET. Хммм ... Думаю, я мог бы уменьшить N на 1 и 2 во втором и третьем SELECT.floor()
? Какие преимущества это дает?Перейдите по этой ссылке, чтобы узнать о различных вариантах. http://www.depesz.com/index.php/2007/09/16/my-oughtts-on-getting-random-row/
Обновить: (А.Хэтчкинс)
Резюме (очень) длинной статьи таково.
Автор перечисляет четыре подхода:
1)
ORDER BY random() LIMIT 1;
- медленный2)
ORDER BY id where id>=random()*N LIMIT 1
- неравномерно, если есть зазоры3) случайный столбец - необходимо время от времени обновлять
4) произвольный произвольный агрегат - хитрый метод, может быть медленным: random () нужно сгенерировать N раз
и предлагает улучшить метод №2, используя
5)
ORDER BY id where id=random()*N LIMIT 1
с последующими запросами, если результат пуст.источник
Самый простой и быстрый способ получить случайную строку - использовать
tsm_system_rows
расширение:CREATE EXTENSION IF NOT EXISTS tsm_system_rows;
Затем вы можете выбрать точное количество строк, которое хотите:
SELECT myid FROM mytable TABLESAMPLE SYSTEM_ROWS(1);
Это доступно в PostgreSQL 9.5 и новее.
См. Https://www.postgresql.org/docs/current/static/tsm-system-rows.html
источник
ORDER BY random() LIMIT 1;
должен работать достаточно быстро.Я придумал очень быстрое решение без
TABLESAMPLE
. Намного быстрее, чемOFFSET random()*N LIMIT 1
. Это даже не требует подсчета таблиц.Идея состоит в том, чтобы, например, создать индекс выражения со случайными, но предсказуемыми данными
md5(primary key)
.Вот тест с образцами данных 1M строк:
create table randtest (id serial primary key, data int not null); insert into randtest (data) select (random()*1000000)::int from generate_series(1,1000000); create index randtest_md5_id_idx on randtest (md5(id::text)); explain analyze select * from randtest where md5(id::text)>md5(random()::text) order by md5(id::text) limit 1;
Результат:
Этот запрос может иногда (с вероятностью примерно 1 / Number_of_rows) возвращать 0 строк, поэтому его необходимо проверить и запустить повторно. Кроме того, вероятности не совсем одинаковы - некоторые строки более вероятны, чем другие.
Для сравнения:
explain analyze SELECT id FROM randtest OFFSET random()*1000000 LIMIT 1;
Результаты сильно различаются, но могут быть довольно плохими:
источник