Как запросить случайную строку в SQL? - PullRequest
476 голосов
/ 21 августа 2008

Как я могу запросить случайную строку (или настолько близкую к действительно случайной, насколько это возможно) в чистом SQL?

Ответы [ 28 ]

2 голосов
/ 12 октября 2012

Как указано в комментарии @ BillKarwin к ответу @ cnu ...

При объединении с LIMIT я обнаружил, что он работает намного лучше (по крайней мере с PostgreSQL 9.1) для соединения со случайным порядком, а не для непосредственного упорядочения фактических строк: например, SELECT * FROM tbl_post AS t JOIN ... JOIN ( SELECT id, CAST(-2147483648 * RANDOM() AS integer) AS rand FROM tbl_post WHERE create_time >= 1349928000 ) r ON r.id = t.id WHERE create_time >= 1349928000 AND ... ORDER BY r.rand LIMIT 100

Просто убедитесь, что 'r' генерирует значение 'rand' для каждого возможного значения ключа в сложном запросе, который связан с ним, но все же ограничивает количество строк 'r', где это возможно.

CAST as Integer особенно полезен для PostgreSQL 9.2, который имеет специальную оптимизацию сортировки для целочисленных и плавающих типов одинарной точности.

1 голос
/ 18 апреля 2017

В SQL Server вы можете комбинировать TABLESAMPLE с NEWID (), чтобы получить довольно хорошую случайность и при этом иметь скорость. Это особенно полезно, если вы действительно хотите только 1 или небольшое количество строк.

SELECT TOP 1 * FROM [table] 
TABLESAMPLE (500 ROWS) 
ORDER BY NEWID()
1 голос
/ 18 июля 2011

Вы также можете попробовать использовать функцию new id().

Просто напишите свой запрос и используйте порядок с помощью функции new id(). Это совершенно случайно.

1 голос
/ 16 апреля 2015

В MSSQL (протестировано на 11.0.5569) с использованием

SELECT TOP 100 * FROM employee ORDER BY CRYPT_GEN_RANDOM(10)

значительно быстрее, чем

SELECT TOP 100 * FROM employee ORDER BY NEWID()
1 голос
/ 29 июля 2014

Еще не видел этот вариант в ответах. У меня было дополнительное ограничение, когда мне нужно было, учитывая начальное начальное число, каждый раз выбирать один и тот же набор строк.

Для MS SQL:

Минимальный пример:

select top 10 percent *
from table_name
order by rand(checksum(*))

Нормализованное время выполнения: 1,00

Пример NewId ():

select top 10 percent *
from table_name
order by newid()

Нормализованное время выполнения: 1,02

NewId() незначительно медленнее, чем rand(checksum(*)), поэтому вы можете не использовать его для больших наборов записей.

Выбор с начальным семенем:

declare @seed int
set @seed = Year(getdate()) * month(getdate()) /* any other initial seed here */

select top 10 percent *
from table_name
order by rand(checksum(*) % seed) /* any other math function here */

Если вам нужно выбрать один и тот же набор для данного семени, похоже, это сработает.

1 голос
/ 21 июля 2013

Для MySQL, чтобы получить случайную запись

 SELECT name
  FROM random AS r1 JOIN
       (SELECT (RAND() *
                     (SELECT MAX(id)
                        FROM random)) AS id)
        AS r2
 WHERE r1.id >= r2.id
 ORDER BY r1.id ASC
 LIMIT 1

Подробнее http://jan.kneschke.de/projects/mysql/order-by-rand/

1 голос
/ 02 июля 2009

Большинство решений здесь направлены на то, чтобы избежать сортировки, но им все равно необходимо последовательно сканировать таблицу.

Существует также способ избежать последовательного сканирования, переключившись на индексное сканирование. Если вы знаете значение индекса вашей случайной строки, вы можете получить результат почти мгновенно. Проблема в том, как угадать значение индекса.

Следующее решение работает на PostgreSQL 8.4:

explain analyze select * from cms_refs where rec_id in 
  (select (random()*(select last_value from cms_refs_rec_id_seq))::bigint 
   from generate_series(1,10))
  limit 1;

В приведенном выше решении вы предполагаете 10 различных случайных значений индекса из диапазона 0 .. [последнее значение id].

Число 10 произвольно - вы можете использовать 100 или 1000, так как оно (как ни удивительно) не оказывает большого влияния на время отклика

Существует также одна проблема - если у вас редкие идентификаторы , вы можете пропустить . Решение состоит в том, чтобы имел план резервного копирования :). В этом случае это чистый старый порядок запроса random (). Когда объединенный идентификатор выглядит так:

explain analyze select * from cms_refs where rec_id in 
    (select (random()*(select last_value from cms_refs_rec_id_seq))::bigint 
     from generate_series(1,10))
    union all (select * from cms_refs order by random() limit 1)
    limit 1;

Не предложение union ALL . В этом случае, если первая часть возвращает какие-либо данные, вторая НИКОГДА не выполняется!

1 голос
/ 20 июля 2010

В конце, но попал сюда через гугл, поэтому ради потомков добавлю альтернативное решение.

Другой подход состоит в том, чтобы использовать TOP дважды, с чередующимися ордерами. Я не знаю, является ли это «чистым SQL», потому что он использует переменную в TOP, но он работает в SQL Server 2008. Вот пример, который я использую для таблицы словарных слов, если я хочу случайное слово. 1003 *

SELECT TOP 1
  word
FROM (
  SELECT TOP(@idx)
    word 
  FROM
    dbo.DictionaryAbridged WITH(NOLOCK)
  ORDER BY
    word DESC
) AS D
ORDER BY
  word ASC

Конечно, @idx - это произвольно сгенерированное целое число в диапазоне от 1 до COUNT (*) на целевой таблице включительно. Если ваш столбец проиндексирован, вы тоже извлечете из него пользу. Еще одним преимуществом является то, что вы можете использовать его в функции, поскольку NEWID () не разрешен.

Наконец, вышеупомянутый запрос выполняется примерно за 1/10 времени выполнения запроса NEWID () для той же таблицы. YYMV.

0 голосов
/ 28 марта 2018

В SQL Server 2012+ вы можете использовать запрос OFFSET FETCH , чтобы сделать это для одной случайной строки

select  * from MyTable ORDER BY id OFFSET n ROW FETCH NEXT 1 ROWS ONLY

где id - это столбец идентификаторов, а n - требуемая строка - рассчитывается как случайное число от 0 до count () - 1 таблицы (смещение 0 - первая строка после всех)

Это работает с дырами в табличных данных, если у вас есть индекс для работы с предложением ORDER BY. Это также очень хорошо для случайности - так как вы сами решаете, что нужно пройти, но срывов в других методах нет. Кроме того, производительность довольно хорошая, на меньшем наборе данных она хорошо держится, хотя я не пробовал серьезных тестов производительности на нескольких миллионах строк.

0 голосов
/ 21 августа 2008

Я должен согласиться с CD-MaN: использование «ORDER BY RAND ()» будет хорошо работать для небольших столов или когда вы делаете SELECT только несколько раз.

Я также использую технику «num_value> = RAND () * ...», и если я действительно хочу получить случайные результаты, у меня есть специальный «случайный» столбец в таблице, который я обновляю раз в день или около того. Этот одиночный прогон UPDATE займет некоторое время (особенно потому, что у вас должен быть индекс для этого столбца), но это намного быстрее, чем создание случайных чисел для каждой строки каждый раз, когда выполняется выбор.

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