Как генерировать случайное число для каждой строки в TSQL Select?
Мне нужно другое случайное число для каждой строки в моей таблице. Следующий, казалось бы, очевидный код использует одно и то же случайное значение для каждой строки.
SELECT table_name, RAND() magic_number
FROM information_schema.tables
Я хотел бы получить INT или FLOAT из этого. Остальная часть истории-я собираюсь использовать это случайное число для создания случайного смещения даты от известной даты, например, 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 () будет генерировать то же случайное число, если используется в запросе выбора таблицы. То же самое относится, если вы используете seed для функции 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 ()
или, возможно, это Выберите binary_checksum (newid ())
проблема, с которой я иногда сталкиваюсь с выбранным "ответом", заключается в том, что распределение не всегда равномерное. Если вам нужно очень равномерное распределение случайных 1-14 среди множества строк, вы можете сделать что-то вроде этого (моя база данных имеет 511 таблиц, поэтому это работает. Если у вас меньше строк, чем у вас случайный диапазон чисел, это не работает хорошо):
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;