Известно, что встроенная функция newID() широко используется разработчиками не только по прямому назначению — то есть для генерации уникальных первичных ключей, но и как средство генерации массивов псевдослучайных данных.
В составе встроенных функций newID() фактически единственная, которая не только недетерминирована, но и можно сказать «супернедетерминирована», поскольку в отличие от всех остальных способна выдавать новое значение для каждой новой строки, а не одно и то же для всей партии — что делает его чрезвычайно полезным для такой массовой генерации.
Помимо newID(), этим свойством обладает и newSequentialID(), но его использование где-либо кроме установки значения по умолчанию для столбцов типа uniqueidentifier запрещено.
За примерами далеко ходить не придется — ниже код:
или этот (если контрольная сумма кажется трудоемкой операцией):SELECT TOP 100 ABS(CHECKSUM(NEWID())) % 1000 FROM sysobjects A CROSS JOIN sysobjects B
SELECT TOP 100 ABS(CONVERT(INT, (CONVERT(BINARY(4), (NEWID()))))) % 1000
FROM sysobjects A
CROSS JOIN sysobjects B
Сгенерирует нам таблицу из 100 случайных целых чисел в диапазоне от 0 до 999. Для плавающих чисел вы можете использовать свойство функции rand() для инициализации генератора целым числом:
SELECT TOP 100 RAND(CHECKSUM(NEWID()))
FROM sysobjects A
CROSS JOIN sysobjects B
В этом случае rand() по сути используется просто как преобразователь диапазона int32 в диапазон [0.1).
Статистическая проверка качества распределения этим методом на количестве записей порядка миллиона показывает, что оно не уступает стандартному использованию rand(), инициализируемому один раз и затем используемому в цикле.
Поэтому вы можете смело его использовать.
Еще один интересный вариант — генерация нормально распределенных данных.
Здесь мы будем использовать метод Бокс-Мюллер : SELECT TOP 1000
COS(2 * PI() * RAND(BINARY_CHECKSUM(NEWID()))) *
SQRT(-2 * LOG(RAND(BINARY_CHECKSUM(NEWID()))))
FROM sysobjects A
CROSS JOIN sysobjects B
CROSS JOIN sysobjects C
Желающие могут проверить, что полученное распределение очень близко к нормальному, построив его график.
Все это хорошо работает и позволяет очень быстро генерировать не менее десятков миллионов записей, не используя лобовых решений типа циклов, курсоров или даже вставки записей одну за другой в базу данных с прикладного уровня.
Вам просто нужно убедиться, что таблицы, которые вы используете в качестве источника строк, имеют достаточную емкость, и либо увеличить количество CROSS JOIN, либо использовать в качестве источника табличные переменные с необходимым количеством строк.
Однако тема не только об этом.
В подавляющем большинстве случаев сгенерированные строки материализуются, то есть вставляются в постоянную или временную таблицу или в табличную переменную.
Если это так, то дальше читать не надо — материализованные данные вполне подойдут. Однако бывают случаи, когда приведенные выше операторы используются в подзапросах.
И вот здесь проявляются особенности поведения SQL-движка, которые сложно объяснить на первый взгляд. Давайте рассмотрим их на примерах, а затем попробуем проанализировать, почему это происходит и как с этим бороться:
Для начала просто напишите в подзапросе оператор с newID() и запустите его несколько раз в цикле: declare @c int = 0
while @c < 5
begin
SELECT *
FROM (
SELECT ABS(CONVERT(INT, CONVERT(BINARY(4), NEWID()))) % 5 AS RNDIDX
FROM (
Теги: #ms sql 2008 #newID() #rand #контрольная сумма #план выполнения #псевдорандом #нормальное распределение #Box-Muller #DataArt #sql #Microsoft SQL Server
-
Сотни Стариков Могут Быть Выброшены На Улицу
19 Oct, 24 -
Киберпанк Приближается
19 Oct, 24 -
Интересное Использование Adblock Plus
19 Oct, 24