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

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

Ответы [ 28 ]

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

См. Этот пост: SQL для выбора случайной строки из таблицы базы данных . Для этого используются методы MySQL, PostgreSQL, Microsoft SQL Server, IBM DB2 и Oracle (по этой ссылке скопировано следующее):

Выберите случайную строку с MySQL:

SELECT column FROM table
ORDER BY RAND()
LIMIT 1

Выберите случайную строку с PostgreSQL:

SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1

Выберите случайную строку в Microsoft SQL Server:

SELECT TOP 1 column FROM table
ORDER BY NEWID()

Выберите случайную строку с помощью IBM DB2

SELECT column, RAND() as IDX 
FROM table 
ORDER BY IDX FETCH FIRST 1 ROWS ONLY

Выберите случайную запись с Oracle:

SELECT column FROM
( SELECT column FROM table
ORDER BY dbms_random.value )
WHERE rownum = 1
172 голосов
/ 21 августа 2008

Решения типа Джеремиса:

SELECT * FROM table ORDER BY RAND() LIMIT 1

работают, но им требуется последовательное сканирование всей таблицы (поскольку необходимо вычислить случайное значение, связанное с каждой строкой, чтобы можно было определить наименьшее), что может быть довольно медленным даже для таблиц среднего размера. Я бы порекомендовал использовать какой-нибудь индексированный числовой столбец (многие таблицы имеют их в качестве первичных ключей), а затем написать что-то вроде:

SELECT * FROM table WHERE num_value >= RAND() * 
    ( SELECT MAX (num_value ) FROM table ) 
ORDER BY num_value LIMIT 1

Работает в логарифмическом времени, независимо от размера таблицы, если num_value проиндексирован. Одно предупреждение: предполагается, что num_value равномерно распределено в диапазоне 0..MAX(num_value). Если ваш набор данных сильно отклоняется от этого предположения, вы получите искаженные результаты (некоторые строки будут появляться чаще других).

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

Я не знаю, насколько это эффективно, но я использовал это раньше:

SELECT TOP 1 * FROM MyTable ORDER BY newid()

Поскольку GUID довольно случайны, порядок означает, что вы получите случайную строку.

26 голосов
/ 21 декабря 2010
ORDER BY NEWID()

занимает 7.4 milliseconds

WHERE num_value >= RAND() * (SELECT MAX(num_value) FROM table)

занимает 0.0065 milliseconds!

Я обязательно пойду с последним методом.

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

Вы не сказали, какой сервер используете. В более старых версиях SQL Server вы можете использовать это:

select top 1 * from mytable order by newid()

В SQL Server 2005 и более поздних версиях вы можете использовать TABLESAMPLE для получения случайной выборки, которую можно повторить:

SELECT FirstName, LastName
FROM Contact 
TABLESAMPLE (1 ROWS) ;
10 голосов
/ 28 мая 2009

для SQL Server

newid () / order by будет работать, но будет очень дорого для больших наборов результатов, потому что он должен генерировать id для каждой строки, а затем сортировать их.

TABLESAMPLE () - это хорошо с точки зрения производительности, но вы получите совокупность результатов (будут возвращены все строки на странице).

Для более эффективной работы истинной случайной выборки лучшим способом является случайная фильтрация строк. Я нашел следующий пример кода в электронной документации по SQL Server Ограничение наборов результатов с помощью TABLESAMPLE :

Если вы действительно хотите случайную выборку отдельные строки, измените ваш запрос на отфильтровывать строки случайным образом, а не используя TABLESAMPLE. Например, следующий запрос использует NEWID функция для возврата примерно одного процентов строк Таблица Sales.SalesOrderDetail:

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float)
              / CAST (0x7fffffff AS int)

Столбец SalesOrderID включен в выражение CHECKSUM так, чтобы NEWID () оценивается один раз в строке достичь выборки для каждого ряда. Выражение CAST (CHECKSUM (NEWID (), SalesOrderID) & 0x7fffffff AS float / CAST (0x7fffffff AS int) оценивается как случайное значение с плавающей точкой от 0 до 1.

Когда я запускаю таблицу с 1 000 000 строк, вот мои результаты:

SET STATISTICS TIME ON
SET STATISTICS IO ON

/* newid()
   rows returned: 10000
   logical reads: 3359
   CPU time: 3312 ms
   elapsed time = 3359 ms
*/
SELECT TOP 1 PERCENT Number
FROM Numbers
ORDER BY newid()

/* TABLESAMPLE
   rows returned: 9269 (varies)
   logical reads: 32
   CPU time: 0 ms
   elapsed time: 5 ms
*/
SELECT Number
FROM Numbers
TABLESAMPLE (1 PERCENT)

/* Filter
   rows returned: 9994 (varies)
   logical reads: 3359
   CPU time: 641 ms
   elapsed time: 627 ms
*/    
SELECT Number
FROM Numbers
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), Number) & 0x7fffffff AS float) 
              / CAST (0x7fffffff AS int)

SET STATISTICS IO OFF
SET STATISTICS TIME OFF

Если вам удастся избежать использования TABLESAMPLE, это даст вам наилучшую производительность. В противном случае используйте метод newid () / filter. newid () / order by должен быть последним средством, если у вас большой набор результатов.

4 голосов
/ 09 января 2011

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

PREPARE RandomRecord FROM "SELECT * FROM table LIMIT ?,1";
SET @n=FLOOR(RAND()*(SELECT COUNT(*) FROM table));
EXECUTE RandomRecord USING @n;
3 голосов
/ 21 августа 2008

Лучший способ - поместить случайное значение в новый столбец только для этой цели и использовать что-то вроде этого (псевдокод + SQL):

randomNo = random()
execSql("SELECT TOP 1 * FROM MyTable WHERE MyTable.Randomness > $randomNo")

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

Для решения newid () может потребоваться полное сканирование таблицы, чтобы каждой строке можно было присвоить новый guid, который будет гораздо менее производительным.

Решение

rand () может вообще не работать (т. Е. С MSSQL), поскольку функция будет оцениваться только один раз, а каждой строке будет присвоено одно и то же "случайное" число.

3 голосов
/ 10 марта 2017

Введено из с использованием RAND (), поскольку это не рекомендуется , вы можете просто получить максимальный ID (= Макс):

SELECT MAX(ID) FROM TABLE;

получить случайное число между 1.Max (= My_Generated_Random)

My_Generated_Random = rand_in_your_programming_lang_function(1..Max);

и затем запустите этот SQL:

SELECT ID FROM TABLE WHERE ID >= My_Generated_Random ORDER BY ID LIMIT 1

Обратите внимание, что он проверит все строки, идентификаторы которых равны или выше, чем выбранное значение. Также можно отследить строку внизу таблицы и получить равный или меньший идентификатор, чем My_Generated_Random, а затем изменить запрос следующим образом:

SELECT ID FROM TABLE WHERE ID <= My_Generated_Random ORDER BY ID DESC LIMIT 1
3 голосов
/ 08 октября 2008

Для SQL Server 2005 и 2008, если нам нужна случайная выборка отдельных строк (из Books Online ):

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), SalesOrderID) & 0x7fffffff AS float)
/ CAST (0x7fffffff AS int)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...