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



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


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

632   15  

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 ограничение результирующих наборов с помощью предложения tablesample:

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

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

столбец SalesOrderID включен в выражение контрольной суммы так, что Функция newid() вычисляется один раз для каждой строки в достигните забора на основание в-строки. Выражение CAST(CHECKSUM(NEWID(), SalesOrderID) & 0x7fffffff как поплавок / 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 должно быть в крайнем случае, если у вас есть большой результирующий набор.

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

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

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

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

SELECT * FROM Table1
  WHERE (ABS(CAST(
  (BINARY_CHECKSUM(*) *
  RAND()) as int)) % 100) < 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, возьмите необходимое число (или все записи) и вместо рандомизации в запросе используйте функцию перемешивания PHP. Я не знаю, есть ли у .net эквивалентная функция, но если она есть, то используйте ее, если вы используете .net

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

Comments

    Ничего не найдено.