Выберите n случайных строк из таблицы SQL Server

у меня есть таблица SQL Server с примерно 50 000 строк в ней. Я хочу выбрать около 5000 из этих строк наугад. Я придумал сложный способ, создав временную таблицу со столбцом "случайное число", скопировав в нее свою таблицу, пройдя через временную таблицу и обновив каждую строку с помощью RAND(), а затем выберите из этой таблицы, где столбец случайных чисел

этот статья предлагают использовать

15 ответов


select top 10 percent * from [yourtable] order by newid()

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

select  * from [yourtable] where [yourPk] in 
(select top 10 percent [yourPk] from [yourtable] order by newid())

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


в зависимости от ваших потребностей, TABLESAMPLE получит вас почти как случайное и лучшее представление. это доступно в MS SQL server 2005 и более поздних версиях.

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

на очень большом столе я тестировал

select top 1 percent * from [tablename] order by newid()

заняло более 20 минут.

select * from [tablename] tablesample(1 percent)

заняло 2 минуты.

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

пожалуйста, имейте в виду, что это не так случайно, как newid() метод, но даст вам достойный отбор.

посмотреть страница MSDN.


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

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

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

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

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

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


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

  SELECT * FROM Table1
  WHERE (ABS(CAST(
  (BINARY_CHECKSUM(*) *
  RAND()) as int)) % 100) < 10

Если вам (в отличие от OP) нужно определенное количество записей (что затрудняет подход к контрольной сумме) и вы хотите более случайную выборку, чем TABLESAMPLE предоставляет сам по себе, а также хотите лучшую скорость, чем контрольная сумма, вы можете обойтись слиянием методов TABLESAMPLE и NEWID (), например:

DECLARE @sampleCount int = 50
SET STATISTICS TIME ON

SELECT TOP (@sampleCount) * 
FROM [yourtable] TABLESAMPLE(10 PERCENT)
ORDER BY NEWID()

SET STATISTICS TIME OFF

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


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

SELECT TOP 5000 * FROM [Table] ORDER BY newid();

обновление

просто попробовал и newid() вызов достаточен - нет необходимости во всех бросках и всей математике.


эта ссылка имеет интересное сравнение между Orderby (NEWID ()) и другими методами для таблиц с 1, 7 и 13 миллионами строк.

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

SELECT TOP 10 PERCENT *
  FROM Table1
  ORDER BY NEWID()

однако запрос NEWID имеет большой недостаток, когда вы используете его для больших таблиц. Предложение ORDER BY вызывает копирование всех строк в таблице в базе данных tempdb, где они сортируются. Это вызывает две проблемы:

  1. операция сортировки обычно имеет высокую стоимость, связанную с ней. Сортировка может использовать много дискового ввода/вывода и может работать в течение длительного времени.
  2. в худшем случае в tempdb может закончиться пространство. В в лучшем случае, tempdb может занять большой объем дискового пространства это никогда не будет исправлено без команды ручного сокращения.

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

SELECT * FROM Table1
  WHERE (ABS(CAST(
  (BINARY_CHECKSUM(*) *
  RAND()) as int)) % 100) < 10

основная идея этого запроса заключается в том, что мы хотим создать случайное число от 0 до 99 для каждой строки в таблице, а затем выбрать все те строки, случайное число которых меньше значения указанного процента. В этом примере мы хотим, чтобы приблизительно 10 процентов строк выбирались случайным образом; поэтому мы выбираем все строки, случайное число которых меньше 10.

пожалуйста, прочитайте полную статью в MSDN.


в MySQL вы можете сделать это:

SELECT `PRIMARY_KEY`, rand() FROM table ORDER BY rand() LIMIT 5000;

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

SELECT TOP [number] 
FROM table_name
ORDER BY RAND(CHECKSUM(*) * RAND())

попробуйте это:

SELECT TOP 10 Field1, ..., FieldN
FROM Table1
ORDER BY NEWID()

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

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

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


это работает для меня:

SELECT * FROM table_name
ORDER BY RANDOM()
LIMIT [number]

похоже, newid () не может использоваться в предложении where, поэтому для этого решения требуется внутренний запрос:

SELECT *
FROM (
    SELECT *, ABS(CHECKSUM(NEWID())) AS Rnd
    FROM MyTable
) vw
WHERE Rnd % 100 < 10        --10%

я использовал его в подзапросе, и он вернул мне те же строки в подзапросе

 SELECT  ID ,
            ( SELECT TOP 1
                        ImageURL
              FROM      SubTable 
              ORDER BY  NEWID()
            ) AS ImageURL,
            GETUTCDATE() ,
            1
    FROM    Mytable

затем я решил с включением переменной родительской таблицы в where

SELECT  ID ,
            ( SELECT TOP 1
                        ImageURL
              FROM      SubTable 
              Where Mytable.ID>0
              ORDER BY  NEWID()
            ) AS ImageURL,
            GETUTCDATE() ,
            1
    FROM    Mytable

обратите внимание, где condtition


используемый серверный язык обработки (например, PHP, .net и т. д.) Не указан, но если это PHP, возьмите необходимое число (или все записи) и вместо рандомизации в запросе используйте функцию shuffle PHP. Я не знаю, имеет ли .net эквивалентную функцию, но если она использует это, если вы используете .net

ORDER BY RAND () может иметь довольно высокую производительность, в зависимости от того, сколько записей задействовано.