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

sql random

403002 просмотра

28 ответа

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

Автор: sverrejoh Источник Размещён: 29.07.2019 08:52

Ответы (28)


687 плюса

Решение

Смотрите этот пост: 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
Автор: Yaakov Ellis Размещён: 21.08.2008 06:32

173 плюса

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

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

Автор: Grey Panther Размещён: 21.08.2008 06:37

59 плюса

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

SELECT TOP 1 * FROM MyTable ORDER BY newid()

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

Автор: Matt Hamilton Размещён: 21.08.2008 06:30

27 плюса

ORDER BY NEWID()

принимает 7.4 milliseconds

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

берет 0.0065 milliseconds!

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

Автор: Neel Размещён: 21.12.2010 07:57

13 плюса

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

select top 1 * from mytable order by newid()

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

SELECT FirstName, LastName
FROM Contact 
TABLESAMPLE (1 ROWS) ;
Автор: Jon Galloway Размещён: 21.08.2008 06:30

10 плюса

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

Автор: Rob Boek Размещён: 28.05.2009 06:23

4 плюса

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

ПОДГОТОВИТЬ RandomRecord FROM "SELECT * FROM table LIMIT?, 1";
SET @ n = FLOOR (RAND () * (ВЫБЕРИТЕ СЧЕТЧИК (*) ИЗ таблицы));
EXECUTE RandomRecord USING @n;
Автор: ldrut Размещён: 09.01.2011 06:49

3 плюса

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

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

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

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

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

Автор: Ishmaeel Размещён: 21.08.2008 06:36

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)
Автор: Santiago Cepas Размещён: 08.10.2008 12:56

3 плюса

Вместо того, чтобы использовать 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
Автор: forsberg Размещён: 10.03.2017 04:19

2 плюса

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

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

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, который имеет специальную оптимизацию сортировки для целочисленных и плавающих типов одинарной точности.

Автор: karmakaze Размещён: 12.10.2012 04:01

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

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

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

Автор: hegemon Размещён: 02.07.2009 01:12

1 плюс

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

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

Автор: alphadogg Размещён: 20.07.2010 02:03

1 плюс

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

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

Автор: Jai - gotaninterviewcall Размещён: 18.07.2011 06:11

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/

Автор: Sophy Размещён: 21.07.2013 12:34

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

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

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

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

Автор: klyd Размещён: 29.07.2014 05:36

1 плюс

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

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

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

SELECT TOP 100 * FROM employee ORDER BY NEWID()
Автор: David Knight Размещён: 16.04.2015 01:36

1 плюс

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

SELECT TOP 1 * FROM [table] 
TABLESAMPLE (500 ROWS) 
ORDER BY NEWID()
Автор: Chris Arbogast Размещён: 18.04.2017 12:51

0 плюса

 SELECT * FROM table ORDER BY RAND() LIMIT 1
Автор: Jeremy Ruten Размещён: 21.08.2008 06:30

0 плюса

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

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

Автор: BlaM Размещён: 21.08.2008 07:20

0 плюса

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

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

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

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

Автор: Sean Turner Размещён: 13.05.2009 02:52

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
Автор: DAVID Размещён: 31.01.2011 10:35

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;
Автор: Aurélien Ooms Размещён: 18.12.2013 02:45

0 плюса

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

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

SELECT *
FROM employee sample(1)
WHERE rownum=1
Автор: sev3ryn Размещён: 03.09.2014 11:00

0 плюса

Для Firebird:

Select FIRST 1 column from table ORDER BY RAND()
Автор: Luigi04 Размещён: 28.11.2016 08:23

0 плюса

В 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. Это также очень хорошо для случайности - так как вы сами решаете, что нужно пройти, но срывов в других методах нет. Кроме того, производительность довольно хорошая, на меньшем наборе данных она хорошо держится, хотя я не пробовал серьезных тестов производительности на нескольких миллионах строк.

Автор: gbjbaanb Размещён: 28.03.2018 07:41

0 плюса

Для 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)
)
Автор: Endri Размещён: 28.05.2018 08:37

-1 плюса

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

SELECT column FROM table
ORDER BY RAND()
LIMIT 1
Автор: nvnvashisth Размещён: 06.07.2018 07:59
Вопросы из категории :
32x32