Как запросить случайную строку в SQL?


410

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

  0

Я всегда делал это в php после результатов запроса из sql ... это, вероятно, намного быстрее для обработки в соответствии с лимитом решения 1 appendage 23 дек. 092009-12-23 20:11:32

  0

[Если SQL Server вы можете использовать агрегат CLR, чтобы избежать ненужных сортировок] (http://stackoverflow.com/questions/5210631/selecting-a-distinct-combination-of-2-columns-in-sql/5210706#5210706) 20 мар. 112011-03-20 15:30:55

+2

Кажется, что нет никакого «чистого SQL» решения, которое работает на каждом dbms ... есть решение для каждого из них. 05 авг. 142014-08-05 16:11:17

567

Смотрите этот пост: SQL to Select a random row from a database table , Он проходит через методу делать это в 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 
+53

+1 для решения более чем одного db. 27 янв. 112011-01-27 22:19:24

+12

-1 для того, чтобы полагаться на' order by rand() 'или эквиваленты во всех dbs: |. [также упоминается здесь] (http://stackoverflow.com/questions/19412/how-to-request-a-random-row-in-sql/19568#comment64838_19568). 26 май. 142014-05-26 09:27:04

+8

Десять лет назад [кто-то сказал] (http://www.titov.net/2005/09/21/do-not-use-order-by-rand-or-how-to-get-random-rows- from-table /), что использование 'ORDER BY RAND()' неверно ... 23 июн. 152015-06-23 06:42:58

  0

ORDER BY NEWID() похоже заметно медленнее на SQL Server. Мой запрос выглядит следующим образом: выбрать топ 1000 c.CustomerID, CL.LoginName от клиента C внутреннее соединение LinkedAccount LA на c.CustomerID = LA.CustomerId внутреннее соединение CL на регистрация клиента c.CustomerID = CL.CustomerId группе C .CustomerId, CL.LoginName с подсчетом (*)> 1 заказ от NEWID() Удаление строки «order by NEWID()» возвращает результаты намного быстрее. 26 авг. 152015-08-26 23:02:40

  0

Для SQLite используйте функцию RANDOM(). 22 окт. 152015-10-22 22:34:44

+2

Эти решения не масштабируются. Они 'O (n)' с 'n' являются числом записей в таблице. Представьте, что у вас 1 миллион записей, вы действительно хотите генерировать 1 миллион случайных чисел или уникальных идентификаторов? Я бы предпочел использовать 'COUNT()' и включить это в новое выражение LIMIT с одним случайным числом. 02 окт. 162016-10-02 11:35:13


58

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

SELECT TOP 1 * FROM MyTable ORDER BY newid() 

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

  0

Это точно так же, как 'ORDER BY RAND() LIMIT 1' 02 дек. 102010-12-02 05:04:14

+5

Это также очень специфично для базы данных, так как использует 'TOP 1' и' newid() '. 08 фев. 112011-02-08 15:02:39

+1

Я использую MS SQL-сервер, SELECT TOP 1 * FROM some_table_name ORDER BY NEWID() работал отлично для меня, спасибо за советы ребятам! 10 окт. 102010-10-10 08:12:47

+7

Плохая идея. Этот метод не будет использовать индекс, если каждый столбец не индексируется indivdually. Таблица с 100 миллионами записей может занять очень много времени, чтобы получить одну запись. 13 дек. 122012-12-13 19:14:44


0
SELECT * FROM table ORDER BY RAND() LIMIT 1 
  0

Десять лет назад (2005) какой-то парень [сказал] (http://www.titov.net/2005/09/21/do- not-use-order-by-rand-or-how-to-get-random-rows-from-table /), что использование 'ORDER BY RAND()' неверно ... 23 июн. 152015-06-23 07:09:13


12

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

select top 1 * from mytable order by newid() 

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

SELECT FirstName, LastName 
FROM Contact 
TABLESAMPLE (1 ROWS) ; 
+8

MSDN говорит NEWID() предпочтительнее TABLESAMPLE по-настоящему случайных результатов: http://msdn.microsoft.com/en-us/library/ms189108.aspx 10 ноя. 082008-11-10 23:02:50

+5

@Andrew Hedges: ORDER BY NEWID() слишком дорогостоящий 04 ноя. 102010-11-04 14:56:57


2

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

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

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

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

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

+1

Обертывание вокруг, когда вы получите 0 результатов обеспечивает доказуемо случайный образец (не только «достаточно хороший»). Это решение * почти * масштабируется для многострочных запросов (думаю, «party shuffle»). Проблема заключается в том, что результаты, как правило, выбираются в тех же группах неоднократно. Чтобы обойти это, вам нужно будет повторно распределить случайные числа, которые вы только что использовали. Вы можете обманывать, отслеживая randomNo и устанавливая его на максимальную (случайность) из результатов, но затем p (строка i в запросе 1 И строка i по запросу 2) == 0, что несправедливо. Позвольте мне сделать математику, и я вернусь к вам с действительно справедливой схемой. 29 окт. 092009-10-29 09:25:40


168

решения, как Йеремис:

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). Если ваш набор данных сильно отклоняется от этого предположения, вы получите искаженные результаты (некоторые строки будут появляться чаще, чем другие).

+7

Второе предложение не случайным. Вы не можете предсказать строку, которая будет выбрана, но если вам нужно было делать ставки, вы бы поставили на вторую строку. И вы никогда не ставите на последнюю строку, тем меньше вероятность того, что будет выбрано распределение вашего num_value и насколько велика ваша таблица. 14 ноя. 102010-11-14 17:38:31

+1

Я знаю, что обычно функции RAND() не очень высокого качества, но кроме этого вы можете объяснить, почему выбор не будет случайным? 16 ноя. 102010-11-16 11:43:21

  0

Я также думаю, что это будет случайным, как 'RAND()' может быть. 17 ноя. 102010-11-17 22:59:56

  0

Можете ли вы объяснить, что такое индексированный числовой столбец? Это как сделать автоматический приращение первичного ключа от 0 до текущего максимума? 22 мар. 112011-03-22 15:14:30

+11

Первая ошибка WRONG в SQL Server. Функция RAND() вызывается только один раз за запрос не один раз в строке. Поэтому он всегда выбирает первую строку (попробуйте). 08 фев. 122012-02-08 21:49:32

+3

Второй также предполагает, что все строки учитываются: возможно, он выберет строку, которая была удалена. 20 фев. 122012-02-20 13:56:52

+3

@ Sam.Rueby Фактически num_value> = RAND() ... limit 1 гарантирует, что пустые строки будут пропущены, пока не найдет существующую строку. 22 июл. 122012-07-22 13:51:44

  0

@ Cd-MaN: Хорошее решение. Есть ли также эффективный способ захвата случайной выборки размера 'n', а не' 1'? ('LIMIT n' приведет к смежным записям) 06 авг. 122012-08-06 12:56:05

  0

Я использовал свой первичный ключ как num_value, но при запуске 'EXPLAIN' на нем говорится, что он все еще проходит через ВСЕ строки ... 20 июн. 132013-06-20 14:34:09

  0

Второе решение выглядит так многообещающе, но это не сработало. На столе с 60 000 записей он всегда выбирал низкие числа. Почти всегда до 1000. Я не знаю, почему. Это то, что действительно работало и было очень случайным: SELECT * FROM questions AS t1 JOIN (SELECT RAND() * (SELECT MAX (id) FROM questions) AS max_id) AS t2 WHERE t1.id> = t2.max_id ORDER BY id LIMIT 1 28 апр. 172017-04-28 03:46:25

  0

Я исправил случай, когда у нас нет одинаково распределенных значений в 'num_value'. Вы можете просмотреть ответ [здесь] (https://stackoverflow.com/a/50562274/4699575) 28 май. 182018-05-28 08:46:57


0

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

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


3

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

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

0

Будьте осторожны, потому что TABLESAMPLE фактически не возвращает случайную выборку строк. Он направляет ваш запрос на случайную выборку страниц 8 КБ, которые составляют вашу строку. Затем ваш запрос выполняется с данными, содержащимися на этих страницах. Из-за того, как данные могут быть сгруппированы на этих страницах (порядок вставки и т. Д.), Это может привести к данным, которые на самом деле не являются случайным образцом.

См: http://www.mssqltips.com/tip.asp?tip=1308

Эта страница MSDN для TABLESAMPLE включает в себя пример того, как генерировать actualy случайную выборку данных.

http://msdn.microsoft.com/en-us/library/ms189108.aspx


9

Для SQL Server

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

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

Для лучшего выполнения истинного случайного образца лучше всего отфильтровать строки случайным образом. Я нашел следующий пример кода в статье SQL Server Books Online Limiting Results Sets by Using TABLESAMPLE:

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

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

В столбце SalesOrderID входят в выражения СУММЫ так, что NEWID() оценивает один раз за строку до , чтобы выполнить выборку для каждой строки. Выражение CAST (СУММА (NEWID(), SalesOrderID) & 0x7fffffff А.С. флоат/ CAST (0x7fffffff AS Int) вычисляется в случайное значение с плавающей точкой между 0 и 1.

При запуске против таблицы с 1000000 строк, вот мои результаты:..

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()/метод фильтра NEWID()/упорядочить по должны быть в последнем случае, если у вас есть большой результирующий набор.


1

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

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

Следующее решение работает на 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, так как оно (удивительно) не оказывает большого влияния на время отклика.

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

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; 

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


1

В последнее время, но здесь, через Google, так что для потомков я добавлю альтернативное решение.

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

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.


20
ORDER BY NEWID() 

принимает 7.4 milliseconds

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

принимает 0.0065 milliseconds!

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

+1

Второй вариант не будет выбирать последнюю строку. Я не знаю, почему, просто указывая на это. 08 окт. 142014-10-08 23:31:26

+4

@Voldemort: 'rand()' возвращает число с плавающей запятой 'n', где' 0 <n <1'. Предполагая, что 'num_value' является целым числом, возвращаемое значение' rand() * max (num_value) 'также будет принудительно привязано к целому числу, что приведет к усечению чего-либо после десятичной точки. Следовательно, 'rand() * max (num_value)' будет ** всегда ** быть меньше, чем 'max (num_value)', поэтому последняя строка никогда не будет выбрана. 18 фев. 152015-02-18 14:57:18

  0

Я не буду эффективен, если мои данные будут удалены часто - если я найду пробел, мне придется повторно запустить весь запрос. 22 май. 172017-05-22 05:26:38


4

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

 
PREPARE RandomRecord FROM "SELECT * FROM table LIMIT ?,1"; 
SET @n=FLOOR(RAND()*(SELECT COUNT(*) FROM table)); 
EXECUTE RandomRecord USING @n; 
  0

Это решение также заботится о возврате случайных строк, когда индексированное числовое значение, используемое в вышеприведенном разделе, не равномерно распределено; так что даже если для этого требуется почти то же (постоянное) время, когда используется id_value> = RAND() * MAX (id_value), это лучше. 08 фев. 112011-02-08 22:33:03

  0

Насколько я могу судить, это не работает в постоянное время, оно работает в линейном времени. В худшем случае @n равно количеству строк в таблице, а «SELECT * FROM table LIMIT ?, 1» оценивает @n - 1 строки, пока не дойдет до последней. 21 сен. 142014-09-21 05:05:45


0

Кажется, что многие из идей, перечисленных до сих пор используют заказ

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

Например (для DB2):

WITH TEMP AS (
SELECT COMLUMN, RAND() AS IDX FROM TABLE) 
SELECT COLUMN FROM TABLE WHERE IDX > .5 
FETCH FIRST 1 ROW ONLY 
+2

После рассмотрения этого решения я нашел фундаментальный недостаток в своей логике. Это будет постоянно возвращать те же самые небольшие значения, близкие к началу таблицы, потому что я предполагаю, что если бы существовало четкое распределение между 0 и 1, существует 50% вероятность того, что первая строка будет соответствовать этим критериям. 31 янв. 112011-01-31 22:59:50


1

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

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


2

Как указано в @ 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

Для 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/

  0

После тестирования многих ответов я считаю, что это лучший. Кажется, что это быстро, и каждый раз выбирает хорошее случайное число. Похоже на второе предложение @GreyPanther выше, но этот ответ выбирает более случайные числа. 28 апр. 172017-04-28 05:30:10


0

простой и эффективный путь от http://akinas.com/pages/en/blog/mysql_random_row/

SET @i = (SELECT FLOOR(RAND() * COUNT(*)) FROM table); PREPARE get_stmt FROM 'SELECT * FROM table LIMIT ?, 1'; EXECUTE get_stmt USING @i; 

1

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

Для 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(*)), поэтому вы можете не использовать его против больших наборов записей.

Выбор Начальной Seed:

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 */ 

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


0

Существует лучшее решение для Oracle вместо использования dbms_random.value, в то время как для полного выполнения заказов требуется выполнить полное сканирование для упорядочивания строк по dbms_random.value, и для больших таблиц это довольно медленно.

Используйте вместо этого:

SELECT * 
FROM employee sample(1) 
WHERE rownum=1 

1

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

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

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

SELECT TOP 100 * FROM employee ORDER BY NEWID() 

0

Для Firebird:

Select FIRST 1 column from table ORDER BY RAND() 

2

Insted из using RAND(), as it is not encouraged, вы можете просто получить максимальную 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 

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

SELECT ID FROM TABLE WHERE ID <= My_Generated_Random ORDER BY ID DESC LIMIT 1 

1

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

SELECT TOP 1 * FROM [table] 
TABLESAMPLE (500 ROWS) 
ORDER BY NEWID()