У меня есть запрос, где я хочу, чтобы результирующие записи были упорядочены случайным образом. Он использует кластерный индекс, поэтому, если я не включу, order by
он, скорее всего, будет возвращать записи в порядке этого индекса. Как я могу обеспечить случайный порядок строк?
Я понимаю, что он, скорее всего, не будет «по-настоящему» случайным, псевдослучайный достаточно хорош для моих нужд.
sql-server
goric
источник
источник
CryptGenRandom
. dba.stackexchange.com/a/208069/3690Первое предложение Прадипа Адиги
ORDER BY NEWID()
, хорошо, и то, что я использовал в прошлом по этой причине.Будьте осторожны с использованием
RAND()
- во многих контекстах он выполняется только один раз для каждого оператора, поэтому неORDER BY RAND()
будет иметь никакого эффекта (поскольку вы получаете одинаковый результат из RAND () для каждой строки).Например:
возвращает каждое имя из нашей таблицы персон и «случайное» число, которое одинаково для каждой строки. Число меняется каждый раз, когда вы запускаете запрос, но одинаково для каждой строки каждый раз.
Чтобы показать, что то же самое относится и к
RAND()
используемому вORDER BY
предложении, я пытаюсь:Результаты по-прежнему упорядочены по имени, указывающему, что более раннее поле сортировки (ожидаемое случайным образом) не имеет никакого эффекта, поэтому предположительно всегда имеет одинаковое значение.
Упорядочение по
NEWID()
работает, хотя, потому что если бы NEWID () не всегда переоценивался, назначение UUID было бы нарушено при вставке множества новых строк в одно состояние с уникальными идентификаторами в качестве ключа, поэтомуделает заказ имен «случайные».
Другие СУБД
Вышесказанное верно для MSSQL (по крайней мере, 2005 и 2008, и, если я правильно помню, 2000). Функция, возвращающая новый UUID, должна оцениваться каждый раз во всех СУБД. NEWID () находится под MSSQL, но это стоит проверить в документации и / или в ваших собственных тестах. Поведение других функций с произвольным результатом, таких как RAND (), с большей вероятностью различается в разных СУБД, поэтому еще раз проверьте документацию.
Также я видел, как упорядочение по значениям UUID игнорируется в некоторых контекстах, поскольку БД предполагает, что тип не имеет значимого упорядочения. Если вы обнаружите, что это именно тот случай, явным образом приведите UUID к строковому типу в предложении упорядочения или оберните вокруг него какую-то другую функцию, как
CHECKSUM()
в SQL Server (может также быть небольшое отличие в производительности, поскольку упорядочение будет выполнено на 32-битные значения, а не 128-битные, хотя перевесит ли выгода от этого затраты на запускCHECKSUM()
одного значения, я оставлю вас на тестирование).Примечание
Если вы хотите произвольное, но несколько повторяющееся упорядочение, упорядочите по некоторому относительно неконтролируемому подмножеству данных в самих строках. Например, либо они, либо они будут возвращать имена в произвольном, но повторяемом порядке:
Произвольные, но повторяемые порядки не часто полезны в приложениях, хотя могут быть полезны при тестировании, если вы хотите протестировать некоторый код на результатах в различных заказах, но хотите иметь возможность повторять каждый прогон несколько раз одинаково (для получения среднего времени результаты за несколько прогонов или тестирование того, что исправление, внесенное вами в код, устраняет проблему или неэффективность, ранее отмеченную определенным входным набором результатов, или просто для проверки того, что ваш код «стабилен», то есть каждый раз возвращает один и тот же результат если отправлены те же данные в заданном порядке).
Этот прием также можно использовать для получения более произвольных результатов от функций, которые не допускают недетерминированные вызовы, такие как NEWID (), в своем теле. Опять же, это не то, что может быть часто полезно в реальном мире, но может пригодиться, если вы хотите, чтобы функция возвращала что-то случайное, а «random-ish» достаточно хорошо (но будьте осторожны, чтобы запомнить правила, которые определяют когда пользовательские функции оцениваются, т. е. обычно только один раз на строку, или ваши результаты могут не соответствовать вашим ожиданиям).
Спектакль
Как указывает EBarr, с любым из вышеперечисленных могут быть проблемы с производительностью. Для более чем нескольких строк вы почти гарантированы, что вывод буферизуется в базу данных tempdb до того, как запрошенное количество строк будет прочитано в правильном порядке, что означает, что даже если вы ищете топ-10, вы можете найти полный индекс сканирование (или, что еще хуже, сканирование таблицы) происходит вместе с огромным блоком записи в базу данных tempdb. Поэтому может быть жизненно важно, как и в большинстве случаев, сравнить с реалистичными данными, прежде чем использовать их в производстве.
источник
Это старый вопрос, но один аспект обсуждения, на мой взгляд, отсутствует - ЭФФЕКТИВНОСТЬ.
ORDER BY NewId()
это общий ответ. Когда фантазии Кто - то получает в них добавить , что вы должны действительно обернутьNewID()
вCheckSum()
, вы знаете, для исполнения!Проблема этого метода заключается в том, что вам по-прежнему гарантируется полное сканирование индекса, а затем полная сортировка данных. Если вы работали с любым серьезным объемом данных, это может быстро стать дорогим. Посмотрите на этот типичный план выполнения и обратите внимание, что сортировка занимает 96% вашего времени ...
Чтобы дать вам представление о том, как это масштабируется, я приведу два примера из базы данных, с которой я работаю.
Order By newid()
по этой таблице генерирует 53 700 операций чтения и занимает 16 секунд.Мораль этой истории в том, что если у вас большие таблицы (например, миллиарды строк) или вам нужно часто выполнять этот запрос,
newid()
метод ломается. Так что же делать мальчику?Познакомьтесь с TABLESAMPLE ()
В SQL 2005 была создана новая возможность под названием
TABLESAMPLE
. Я только видел одну статью, обсуждающую его использование ... должно быть больше. Документы MSDN здесь . Первый пример:Идея, лежащая в основе выборки таблицы, заключается в том, чтобы дать вам приблизительно размер подмножества, о котором вы просите. SQL нумерует каждую страницу данных и выбирает X процентов этих страниц. Фактическое количество возвращаемых строк может варьироваться в зависимости от того, что существует на выбранных страницах.
Так как мне это использовать? Выберите размер подмножества, который превышает количество строк, которое вам нужно, затем добавьте
Top()
. Идея заключается в том что вы можете сделать ваш Ginormous таблица выглядит меньше до к дорогому рода.Лично я использовал это, чтобы фактически ограничить размер моего стола. Таким образом, выполнение этой таблицы строк с миллионами
top(20)...TABLESAMPLE(20 PERCENT)
запросов сокращается до 5600 операций чтения за 1600 мс. Существует такжеREPEATABLE()
опция, где вы можете передать «Семя» для выбора страницы. Это должно привести к стабильному выбору образца.Во всяком случае, просто подумал, что это должно быть добавлено к обсуждению. Надеюсь, это кому-нибудь поможет.
источник
TABLESAMPLE()
зависимости от того, сколько данных у вас есть. Я не думаю, что этоTABLESAMPLE(x ROWS)
могло бы гарантировать, что хотя быx
строки будут возвращены, потому что документация гласит: «Фактическое количество возвращаемых строк может значительно различаться. Если вы укажете небольшое число, например 5, вы можете не получить результаты в образце ». - значит,ROWS
синтаксис все ещеPERCENT
внутри маскируется ?Многие таблицы имеют относительно плотный (несколько пропущенных значений) индексированный числовой идентификатор столбца.
Это позволяет нам определять диапазон существующих значений и выбирать строки, используя случайно сгенерированные значения идентификаторов в этом диапазоне. Это работает лучше всего, когда количество возвращаемых строк относительно невелико, а диапазон значений идентификаторов густо заполнен (поэтому вероятность создания пропущенного значения достаточно мала).
Чтобы проиллюстрировать это, следующий код выбирает 100 различных случайных пользователей из таблицы переполнения стека пользователей, которая содержит 8 123 937 строк.
Первым шагом является определение диапазона значений идентификатора, эффективная операция за счет индекса:
План читает по одной строке с каждого конца индекса.
Теперь мы генерируем 100 различных случайных идентификаторов в диапазоне (с соответствующими строками в таблице пользователей) и возвращаем эти строки:
План показывает, что в этом случае было необходимо 601 случайное число, чтобы найти 100 подходящих строк. Это довольно быстро:
Попробуйте это в Stack Exchange Data Explorer.
источник
Как я объяснил в этой статье , чтобы перетасовать набор результатов SQL, вам нужно использовать вызов функции для конкретной базы данных.
Итак, предположим, что у нас есть следующая таблица базы данных:
И следующие строки в
song
таблице:В SQL Server вам необходимо использовать
NEWID
функцию, как показано в следующем примере:При выполнении вышеупомянутого SQL-запроса на SQL Server мы получим следующий набор результатов:
источник