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

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

28 ответов


посмотреть этот пост: 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

решения, такие как Jeremies:

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


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

SELECT TOP 1 * FROM MyTable ORDER BY newid()

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


ORDER BY NEWID()

принимает 7.4 milliseconds

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

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

Я определенно пойду с последним методом.


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

select top 1 * from mytable order by newid()

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

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

для SQL Server

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

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

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

Если вы действительно хотите случайную выборку отдельные строки, измените запрос на отфильтруйте строки случайным образом, а не использование TABLESAMPLE. Например, следующий запрос использует NEWID функция возвращает примерно процент строк Продажи.Таблица 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 как float / CAST (0x7fffffff как int) вычисляется как случайное значение float между 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 должен быть последним средством, если у вас есть большой набор результатов.


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

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

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

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

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

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

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


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

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

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

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

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

при объединении с лимитом я обнаружил, что он работает намного лучше (по крайней мере, с PostgreSQL 9.1), чтобы присоединиться к случайному порядку, а не напрямую упорядочивать фактические строки: например

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

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


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

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

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

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

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;

не Союз все предложения. В этом случае, если первая часть возвращает какие-либо данные, вторая никогда не выполняется!


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

другой подход - использовать TOP дважды, с чередующимися ордерами. Я не знаю, является ли он "чистым SQL", потому что он использует переменную в верхней части, но он работает в 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.


вы также можете попробовать использовать .

просто напишите свой запрос и используйте order by . Это совершенно случайно.


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


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

для 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 */

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


в MSSQL (проверено на 11.0.5569) с помощью

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

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

SELECT TOP 100 * FROM employee ORDER BY NEWID()

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

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

 SELECT * FROM table ORDER BY RAND() LIMIT 1

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

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


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

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

эта страница MSDN для TableSample приведен пример генерации фактической случайной выборки данных.

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


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

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

простой и эффективный способ от 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;

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

используйте этот код:

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

Для Firebird:

Select FIRST 1 column from table ORDER BY RAND()

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

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

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

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


для SQL Server 2005 и выше, расширение ответа @GreyPanther для случаев, когда num_value не непрерывные значения. Это также работает для случаев, когда у нас нет равномерно распределенных наборов данных и когда num_value - это не число, а уникальный идентификатор.

WITH CTE_Table (SelRow, num_value) 
AS 
(
    SELECT ROW_NUMBER() OVER(ORDER BY ID) AS SelRow, num_value FROM table
) 

SELECT * FROM table Where num_value = ( 
    SELECT TOP 1 num_value FROM CTE_Table  WHERE SelRow >= RAND() * (SELECT MAX(SelRow) FROM CTE_Table)
)

случайная функция из sql может помочь. Также если вы хотите ограничить только одну строку, просто добавьте в конце.

SELECT column FROM table
ORDER BY RAND()
LIMIT 1