Я хочу случайный выбор строк в PostgreSQL, я попробовал это:
select * from table where random() < 0.01;
Но некоторые другие рекомендуют это:
select * from table order by random() limit 1000;
У меня очень большая таблица с 500 миллионами строк, я хочу, чтобы она была быстрой.
Какой подход лучше? В чем различия? Каков наилучший способ выбора случайных строк?
sql
performance
postgresql
random
nanounanue
источник
источник
Ответы:
Учитывая ваши спецификации (плюс дополнительная информация в комментариях),
Приведенный ниже запрос не требует последовательного сканирования большой таблицы, только сканирование индекса.
Сначала получите оценки для основного запроса:
Единственная, возможно, дорогая часть - это
count(*)
(для огромных столов). Приведенные выше характеристики вам не нужны. Оценка подойдет просто отлично, доступна практически бесплатно ( подробное объяснение здесь ):Пока этот запрос
ct
не намного меньшеid_span
, запрос превзойдет другие подходы.Генерация случайных чисел в
id
пространстве. У вас есть «несколько пробелов», поэтому добавьте 10% (достаточно, чтобы легко покрыть пробелы) к числу строк для извлечения.Каждый
id
может быть выбран несколько раз случайно (хотя очень маловероятно с большим пространством идентификаторов), поэтому сгруппируйте сгенерированные числа (или используйтеDISTINCT
).Присоединяйся
id
к большому столу. Это должно быть очень быстро с индексом на месте.Наконец обрежьте излишки
id
, которые не были съедены обманщиками и пробелами. Каждый ряд имеет абсолютно равные шансы быть выбранным.Укороченная версия
Вы можете упростить этот запрос. CTE в запросе выше только для образовательных целей:
Уточнение с помощью rCTE
Особенно, если вы не уверены в пробелах и оценках.
Мы можем работать с меньшим излишком в базовом запросе. Если пропусков слишком много и мы не находим достаточно строк в первой итерации, rCTE продолжает итерацию с рекурсивным членом. Нам все еще нужно относительно небольшое количество пробелов в пространстве идентификаторов, иначе рекурсия может иссякнуть до того, как будет достигнут предел, или мы должны начать с достаточно большого буфера, который не отвечает цели оптимизации производительности.
Дубликаты устраняются
UNION
в rCTE.Внешнее
LIMIT
заставляет CTE останавливаться, как только у нас появляется достаточно строк.Этот запрос тщательно разработан, чтобы использовать доступный индекс, генерировать фактически случайные строки и не останавливаться до тех пор, пока мы не выполним ограничение (если рекурсия не иссякнет). Здесь есть ряд подводных камней, если вы собираетесь переписать его.
Оберните в функцию
Для многократного использования с различными параметрами:
Вызов:
Вы могли бы даже сделать это универсальным для работы с любой таблицей: возьмите имя столбца PK и таблицы как полиморфный тип и используйте
EXECUTE
... Но это выходит за рамки этого вопроса. Видеть:Возможная альтернатива
Если ваши требования позволяют идентичные наборы для повторных вызовов (и мы говорим о повторных вызовах), я бы рассмотрел материализованное представление . Выполните вышеуказанный запрос один раз и запишите результат в таблицу. Пользователи получают квази-случайный выбор со скоростью молнии. Обновите ваш случайный выбор через интервалы или события по вашему выбору.
Postgres 9.5 представляет
TABLESAMPLE SYSTEM (n)
Где
n
процент. Руководство:Жирный акцент мой. Это очень быстро , но результат не совсем случайный . Руководство снова:
Количество возвращаемых строк может сильно отличаться. Для нашего примера, чтобы получить примерно 1000 строк:
Связанный:
Или установите дополнительный модуль tsm_system_rows, чтобы точно получить количество запрошенных строк (если их достаточно) и учесть более удобный синтаксис:
Смотрите ответ Эвана для деталей.
Но это все еще не совсем случайно.
источник
JOIN bigtbl t
это сокращение отJOIN bigtbl AS t
.t
это псевдоним таблицы дляbigtbl
. Его цель - сократить синтаксис, но в этом конкретном случае он не понадобится. Я упростил запрос в своем ответе и добавил простую версию.Вы можете изучить и сравнить план выполнения обоих с помощью
Быстрый тест на большой таблице 1 показывает, что
ORDER BY
первая сортирует полную таблицу, а затем выбирает первые 1000 элементов. Сортировка большой таблицы не только читает эту таблицу, но также включает чтение и запись временных файлов.where random() < 0.1
Сканирует только полную таблицу один раз.Для больших таблиц это может быть не то, что вам нужно, так как даже одно полное сканирование таблицы может занять много времени.
Третье предложение будет
Он останавливает сканирование таблицы, как только будет найдено 1000 строк, и поэтому возвращается быстрее. Конечно, это немного сбивает с толку случайность, но, возможно, это достаточно хорошо в вашем случае.
Изменить: Помимо этого соображения, вы можете проверить уже заданные вопросы для этого. Использование запроса
[postgresql] random
возвращает довольно много хитов.И связанная статья зависимость с изложением еще нескольких подходов:
1 «большой», как в «полная таблица не помещается в память».
источник
random() < 0.02
и затем перетасовать этот списокlimit 1000
! Сортировка будет дешевле на несколько тысяч рядов (смеется).postgresql order by random (), выберите строки в случайном порядке:
порядок postgresql методом random () с
postgresql порядок случайного ограничения одной строкой:
источник
select your_columns from your_table ORDER BY random() limit 1
займет ~ 2 минуты, чтобы выполнить на 45-миллиметровых рядахНачиная с PostgreSQL 9.5, появился новый синтаксис, предназначенный для получения случайных элементов из таблицы:
Этот пример даст вам 5% элементов из
mytable
.Более подробное объяснение см. В этом сообщении в блоге: http://www.postgresql.org/docs/current/static/sql-select.html.
источник
TABLESAMPLE SYSTEM_ROWS(400)
чтобы получить выборку из 400 случайных строк. Вам нужно включить встроенноеtsm_system_rows
расширение, чтобы использовать этот оператор.Тот, у которого ORDER BY, будет медленнее.
select * from table where random() < 0.01;
идет запись за записью, и решает случайным образом отфильтровать его или нет. Это произойдетO(N)
потому, что нужно проверять каждую запись только один раз.select * from table order by random() limit 1000;
собирается отсортировать всю таблицу, а затем выбрать первые 1000. Помимо магии вуду за кулисами, порядок поO(N * log N)
.Недостатком
random() < 0.01
является то, что вы получите переменное количество выходных записей.Обратите внимание, что есть лучший способ перетасовки набора данных , чем сортировка случайные: The Fisher-Yates Перемешать , которая проходит в
O(N)
. Однако реализация shuffle в SQL кажется довольно сложной задачей.источник
Вот решение, которое работает для меня. Я думаю, это очень просто понять и выполнить.
источник
ORDER BY random()
работает, но может быть неэффективным при работе с большим столом.Если вы знаете, сколько строк вы хотите, проверьте
tsm_system_rows
.tsm_system_rows
Сначала установите расширение
Тогда ваш запрос,
источник
SYSTEM
методом.tsm_system_rows
иtsm_system_time
расширений. Насколько я вижу, они практически бесполезны для чего-либо, кроме абсолютно минимального выбора случайных строк. Я был бы признателен, если бы вы могли быстро взглянуть и прокомментировать обоснованность или нет моего анализа.Если вам нужна только одна строка, вы можете использовать вычисленную
offset
производную отcount
.источник
Возможно изменение материализованного представления «Возможная альтернатива», намеченного Эрвином Брандштеттером .
Скажем, например, что вы не хотите дубликатов в возвращаемых рандомизированных значениях. Поэтому вам нужно будет установить логическое значение в основной таблице, содержащей ваш (нерандомизированный) набор значений.
Предполагая, что это входная таблица:
Заполните
ID_VALUES
таблицу по мере необходимости. Затем, как описано Эрвином, создайте материализованное представление, которое рандомизируетID_VALUES
таблицу один раз:Обратите внимание, что материализованное представление не содержит используемый столбец, поскольку он быстро устареет. Представление также не должно содержать другие столбцы, которые могут находиться в
id_values
таблице.Для того чтобы получить (и «потреблять») случайные значения, использовать обновленную-Возвратившись
id_values
, выбираяid_values
изid_values_randomized
с объединением, и применяя требуемые критерии для получения только соответствующие возможности. Например:При
LIMIT
необходимости измените - если вам нужно только одно случайное значение за раз, изменитеLIMIT
на1
.id_values
Я считаю, что при правильных индексах UPDATE-RETURNING должен выполняться очень быстро с небольшой нагрузкой. Возвращает рандомизированные значения с одним обращением к базе данных. Критерии для «приемлемых» строк могут быть настолько сложными, насколько это необходимо. Новые строки могут быть добавлены вid_values
таблицу в любое время, и они станут доступны приложению, как только обновится материализованное представление (которое, вероятно, может быть запущено в непиковое время). Создание и обновление материализованного представления будет медленным, но его нужно выполнять только при добавлении новых идентификаторов вid_values
таблицу.источник
Один урок из моего опыта:
offset floor(random() * N) limit 1
не быстрее чемorder by random() limit 1
.Я думал, что такой
offset
подход будет быстрее, потому что он сэкономит время сортировки в Postgres. Оказывается, это не так.источник
Добавьте столбец
r
с типомserial
. Индексr
.Предположим, у нас есть 200 000 строк, мы собираемся сгенерировать случайное число
n
, где 0n
<<= 200 000.Выделите строки с помощью
r > n
, отсортируйте ихASC
и выберите самую маленькую.Код:
Код не требует пояснений. Подзапрос в середине используется для быстрой оценки количества строк таблицы по адресу https://stackoverflow.com/a/7945274/1271094 .
На уровне приложения вам нужно выполнить инструкцию еще раз, если
n
> количество строк или нужно выбрать несколько строк.источник
Я знаю, что немного опоздал на вечеринку, но я нашел этот замечательный инструмент под названием pg_sample :
Я попробовал это с базой данных на 350 миллионов строк, и это было действительно быстро, не знаю, что такое случайность .
источник