Как создать случайное число для каждой строки в TSQL Select?


Мне нужно другое случайное число для каждой строки в моей таблице. Следующий, казалось бы, очевидный код использует одно и то же случайное значение для каждой строки.

SELECT table_name, RAND() magic_number 
FROM information_schema.tables 

Я хотел бы получить INT или поплавок из этого. Остальная часть истории заключается в том, что я собираюсь использовать это случайное число для создания случайного смещения даты от известной даты, например, 1-14 дней от даты начала.

Это для Microsoft SQL Server 2000.

16 259

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:

  1. CAST(): https://docs.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql
  2. RAND(): http://msdn.microsoft.com/en-us/library/ms177610.aspx
  3. CHECKSUM(): http://msdn.microsoft.com/en-us/library/ms189788.aspx
  4. NEWID(): https://docs.microsoft.com/en-us/sql/t-sql/functions/newid-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

вам нужно будет вызвать RAND () для каждой строки. Вот хороший пример

https://web.archive.org/web/20090216200320/http://dotnet.org.za/calmyourself/archive/2007/04/13/sql-rand-trap-same-value-per-row.aspx

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

Я получаю этот результат, показывая мне, что мое случайное число очень равномерно распределено между многими строками:

enter image description here

select ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) as [Randomizer]

всегда работал на меня

    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;