Как генерировать случайное число для каждой строки в 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:

  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 () будет генерировать то же случайное число, если используется в запросе выбора таблицы. То же самое относится, если вы используете 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

вам нужно будет вызвать 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 ()

или, возможно, это Выберите 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

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

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;