Как создать случайное число для каждой строки в TSQL Select?
Мне нужно другое случайное число для каждой строки в моей таблице. Следующий, казалось бы, очевидный код использует одно и то же случайное значение для каждой строки.
SELECT table_name, RAND() magic_number
FROM information_schema.tables
Я хотел бы получить INT или поплавок из этого. Остальная часть истории заключается в том, что я собираюсь использовать это случайное число для создания случайного смещения даты от известной даты, например, 1-14 дней от даты начала.
Это для Microsoft SQL Server 2000.
16 ответов:
посмотри SQL Server-набор случайных чисел на основе, который имеет очень подробное объяснение.
подводя итог, следующий код генерирует случайное число от 0 до 13 включительно с нормализованным распределением:
ABS(CHECKSUM(NewId())) % 14
изменить свой выбор, просто измените число в конце выражения. Будьте особенно осторожны, если вам нужен диапазон, который включает в себя как положительные, так и отрицательные числа. Если вы сделаете это неправильно, можно дважды пересчитать число 0.
небольшое предупреждение для математических орехов в комнате: есть очень небольшое смещение в этом коде.
CHECKSUM()
результаты в числах, которые являются однородными во всем диапазоне типа данных sql Int, или, по крайней мере, так близко, как может показать мое (редактор) тестирование. Однако будет некоторое смещение, когда CHECKSUM() создает число в самом верхнем конце этого диапазона. Каждый раз, когда вы получаете число между максимально возможным целым числом и последним точным кратным размеру желаемого диапазона (14 в этом случае) перед этим максимальным целым числом эти результаты предпочтительнее оставшейся части вашего диапазона, которая не может быть получена из этого последнего кратного 14.в качестве примера, представьте, что весь диапазон типа Int составляет всего 19. 19-это максимальное целое число, которое вы можете провести. Когда контрольная сумма() приводит к 14-19, они соответствуют результатам 0-5. Эти цифры будут сильно выступает за 6-13, потому что контрольная сумма() дважды как правоподобн для того, чтобы генерировать их. Это легче продемонстрировать визуально. Ниже приведен весь возможный набор результатов для нашего мнимого целого диапазона:
Checksum Integer: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Range Result: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 3 4 5вы можете видеть здесь, что есть больше шансов произвести некоторые числа, чем другие: смещение. К счастью, фактический диапазон типа Int -много больше... настолько, что в большинстве случаев смещение почти незаметно. Тем не менее, это то, что нужно знать, если вы когда-нибудь обнаружите, что делаете это для серьезного кода безопасности.
при вызове несколько раз в одной партии, rand() возвращает тот же номер.
Я бы предложил использовать convert(
varbinary
,newid()
) в качестве аргумента семя:SELECT table_name, 1.0 + floor(14 * RAND(convert(varbinary, newid()))) magic_number FROM information_schema.tables
newid()
гарантированно возвращает другое значение каждый раз, когда он вызывается, даже в пределах той же партии, поэтому использование его в качестве семени предложит rand() давать другое значение каждый раз.отредактировано, чтобы получить случайное целое число от 1 до 14.
RAND(CHECKSUM(NEWID()))
выше будет генерировать (псевдо-) случайное число между 0 и 1, эксклюзив. Если используется в select, поскольку начальное значение изменяется для каждой строки, оно будет генерировать новое случайное число для каждой строки (однако не гарантируется создание уникального числа для каждой строки).
пример в сочетании с верхним пределом 10 (производит числа 1-10):
CAST(RAND(CHECKSUM(NEWID())) * 10 as INT) + 1
документация по Transact-SQL:
генерация случайных чисел между 1000 и 9999 включительно:
FLOOR(RAND(CHECKSUM(NEWID()))*(9999-1000+1)+1000)
"+1 " - для включения значений верхней границы(9999 для предыдущего примера)
отвечая на старый вопрос, но этот ответ не был предоставлен ранее, и, надеюсь, это будет полезно для кого-то найти эти результаты через поисковую систему.
в SQL Server 2008 была введена новая функция,
CRYPT_GEN_RANDOM(8)
, который использует CryptoAPI для получения криптографически сильного случайного числа, возвращается какVARBINARY(8000)
. Вот страница документации: https://docs.microsoft.com/en-us/sql/t-sql/functions/crypt-gen-random-transact-sqlтаким образом, чтобы получить случайное число, вы можете просто вызвать функцию и привести ее к нужному типу:
select CAST(CRYPT_GEN_RANDOM(8) AS bigint)
или
float
между -1 и +1, вы могли бы сделать что-то вроде этого:select CAST(CRYPT_GEN_RANDOM(8) AS bigint) % 1000000000 / 1000000000.0
функция Rand () генерирует то же самое случайное число, если оно используется в запросе выбора таблицы. То же самое применяется, если вы используете семя для функции Rand. Альтернативный способ сделать это, используя это:
SELECT ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) AS [RandomNumber]
получил информацию от здесь, который очень хорошо объясняет проблему.
есть ли у вас целочисленное значение в каждой строке, которое вы могли бы передать в качестве семени функции RAND?
чтобы получить целое число между 1 и 14 я считаю, что это будет работать:
FLOOR( RAND(<yourseed>) * 14) + 1
Если вам нужно сохранить свое семя, чтобы оно каждый раз генерировало "одинаковые" случайные данные, вы можете сделать следующее:
1. Создайте представление, которое возвращает select rand ()
if object_id('cr_sample_randView') is not null begin drop view cr_sample_randView end go create view cr_sample_randView as select rand() as random_number go
2. Создайте UDF, который выбирает значение из представления.
if object_id('cr_sample_fnPerRowRand') is not null begin drop function cr_sample_fnPerRowRand end go create function cr_sample_fnPerRowRand() returns float as begin declare @returnValue float select @returnValue = random_number from cr_sample_randView return @returnValue end go
3. Перед выбором данных введите значение функции rand (), а затем используйте UDF в операторе select.
select rand(200); -- see the rand() function with cte(id) as (select row_number() over(order by object_id) from sys.all_objects) select id, dbo.cr_sample_fnPerRowRand() from cte where id <= 1000 -- limit the results to 1000 random numbers
попробуйте использовать начальное значение в RAND (seedInt). RAND () будет выполняться только один раз для каждого оператора, поэтому вы видите один и тот же номер каждый раз.
Если вам не нужно, чтобы это было целое число, но любой случайный уникальный идентификатор, вы можете использовать
newid()
SELECT table_name, newid() magic_number FROM information_schema.tables
select round(rand(checksum(newid()))*(10)+20,2)
здесь случайное число будет между 20 и 30.
round
даст максимум два знака после запятой.Если вы хотите отрицательные числа, вы можете сделать это с
select round(rand(checksum(newid()))*(10)-60,2)
тогда минимальное значение будет -60, а максимальное -50.
выберите newid ()
или, возможно, это select binary_checksum (newid ())
проблема, которую я иногда имею с выбранным "ответом", заключается в том, что распределение не всегда равномерное. Если вам нужно очень равномерное распределение случайных 1-14 среди большого количества строк, вы можете сделать что-то вроде этого (моя база данных имеет 511 таблиц, так что это работает. Если у вас меньше строк, чем у случайного числа span, это не работает хорошо):
SELECT table_name, ntile(14) over(order by newId()) randomNumber FROM information_schema.tables
этот вид делает противоположность нормальным случайным решениям в том смысле, что он сохраняет последовательность чисел и рандомизирует другие колонна.
помните, у меня есть 511 таблиц в моей базе данных (что имеет отношение только к b/c мы выбираем из information_schema). Если я беру предыдущий запрос и помещаю его во временную таблицу #X, а затем запускаю этот запрос на результирующие данные:
select randomNumber, count(*) ct from #X group by randomNumber
Я получаю этот результат, показывая мне, что мое случайное число очень равномерно распределено между многими строками:
DROP VIEW IF EXISTS vwGetNewNumber; GO Create View vwGetNewNumber as Select CAST(RAND(CHECKSUM(NEWID())) * 62 as INT) + 1 as NextID, 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'as alpha_num; ---------------CTDE_GENERATE_PUBLIC_KEY ----------------- DROP FUNCTION IF EXISTS CTDE_GENERATE_PUBLIC_KEY; GO create function CTDE_GENERATE_PUBLIC_KEY() RETURNS NVARCHAR(32) AS BEGIN DECLARE @private_key NVARCHAR(32); set @private_key = dbo.CTDE_GENERATE_32_BIT_KEY(); return @private_key; END; go ---------------CTDE_GENERATE_32_BIT_KEY ----------------- DROP FUNCTION IF EXISTS CTDE_GENERATE_32_BIT_KEY; GO CREATE function CTDE_GENERATE_32_BIT_KEY() RETURNS NVARCHAR(32) AS BEGIN DECLARE @public_key NVARCHAR(32); DECLARE @alpha_num NVARCHAR(62); DECLARE @start_index INT = 0; DECLARE @i INT = 0; select top 1 @alpha_num = alpha_num from vwGetNewNumber; WHILE @i < 32 BEGIN select top 1 @start_index = NextID from vwGetNewNumber; set @public_key = concat (substring(@alpha_num,@start_index,1),@public_key); set @i = @i + 1; END; return @public_key; END; select dbo.CTDE_GENERATE_PUBLIC_KEY() public_key;