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

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

SELECT table_name, RAND() magic_number 
FROM information_schema.tables 

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

Это для Microsoft SQL Server 2000.

Ответ 1

Взгляните на SQL Server - установите случайные числа на основе, которые имеют очень подробное объяснение.

Подводя итог, следующий код генерирует случайное число от 0 до 13 включительно с нормализованным распределением:

ABS(CHECKSUM(NewId())) % 14

Чтобы изменить свой диапазон, просто измените число в конце выражения. Будьте особенно осторожны, если вам нужен диапазон, который включает как положительные, так и отрицательные числа. Если вы сделаете это неправильно, можно дважды считать число 0.

Небольшое предупреждение для математических орехов в комнате: в этом коде есть небольшое смещение. CHECKSUM() приводит к числам, которые являются одинаковыми по всему диапазону типа данных sql Int, или, по крайней мере, настолько близкими, насколько может показать мое (редакторское) тестирование. Однако будет некоторое смещение, когда CHECKSUM() выдаст число в самом верхнем конце этого диапазона. Каждый раз, когда вы получаете число между максимально возможным целым числом и последним точным кратным размера желаемого диапазона (в данном случае 14) перед этим максимальным целым числом, эти результаты предпочтительнее, чем оставшаяся часть вашего диапазона, которая не может быть получена из это последнее кратное 14.

Например, представьте, что весь диапазон типа Int равен только 19. 19 - максимально возможное целое число, которое вы можете удерживать. Когда CHECKSUM() приводит к 14-19, они соответствуют результатам 0-5. Эти цифры будут более предпочтительными после 6-13, потому что CHECKSUM() в два раза чаще генерирует их. Это проще продемонстрировать наглядно. Ниже представлен весь возможный набор результатов для нашего воображаемого целочисленного диапазона:

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 намного больше... настолько, что в большинстве случаев смещение почти невозможно обнаружить. Однако об этом следует знать, если вы когда-нибудь обнаружите, что делаете это для серьезного кода безопасности.

Ответ 2

Когда вы вызываете несколько раз в одной партии, 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.

Ответ 3

RAND(CHECKSUM(NEWID()))

Выше будет генерировать (pseudo-) случайное число от 0 до 1, исключая. Если используется в выборе, поскольку начальное значение изменяется для каждой строки, оно генерирует новое случайное число для каждой строки (однако, не гарантируется, что будет генерироваться уникальное число для каждой строки).

Пример в сочетании с верхним пределом 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

Ответ 4

Генерация случайных чисел от 1000 до 9999 включительно:

FLOOR(RAND(CHECKSUM(NEWID()))*(9999-1000+1)+1000)

"+1" - включить значения верхней границы (9999 для предыдущего примера)

Ответ 5

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

В 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

Ответ 6

Функция Rand() сгенерирует то же случайное число, если оно используется в запросе SELECT таблицы. То же самое относится, если вы используете семя для функции Rand. Альтернативный способ сделать это, используя это:

SELECT ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) AS [RandomNumber]

Получил отсюда информацию, которая очень хорошо объясняет проблему.

Ответ 7

У вас есть целочисленное значение в каждой строке, которое вы могли бы передать как семя в функцию RAND?

Чтобы получить целое число от 1 до 14, я считаю, что это сработает:

FLOOR( RAND(<yourseed>) * 14) + 1

Ответ 8

Если вам нужно сохранить свое семя, чтобы каждый раз генерировать "одинаковые" случайные данные, вы можете сделать следующее:

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

Ответ 9

попробуйте использовать начальное значение в RAND (seedInt). RAND() будет выполняться только один раз для каждого оператора, поэтому каждый раз вы видите один и тот же номер.

Ответ 10

Если вам не нужно, чтобы это целое число, но любой случайный уникальный идентификатор, вы можете использовать newid()

SELECT table_name, newid() magic_number 
FROM information_schema.tables

Ответ 12

select round(rand(checksum(newid()))*(10)+20,2)

Здесь случайное число будет находиться между 20 и 30. round даст максимум два десятичных знака.

Если вы хотите отрицательные числа, вы можете сделать это с помощью

select round(rand(checksum(newid()))*(10)-60,2)

Тогда минимальное значение будет -60, а max будет -50.

Ответ 13

выберите newid()

или, возможно, этот select binary_checksum (newid())

Ответ 14

Проблема, с которой я иногда сталкиваюсь с выбранным "Ответ", заключается в том, что распределение не всегда равно. Если вам нужно очень равномерное распределение случайных 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

Ответ 15

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

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

Ответ 16

Это так же просто, как:

DECLARE @rv FLOAT;
SELECT @rv = rand();

И это поместит в таблицу случайное число от 0 до 99:

CREATE TABLE R
(
    Number int
)

DECLARE @rv FLOAT;
SELECT @rv = rand();

INSERT INTO dbo.R
(Number)
    values((@rv * 100));

SELECT * FROM R

Ответ 17

    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;

Ответ 18

Обновить набор my_table my_field = CEILING ((RAND (CAST (NEWID() AS varbinary)) * 10))

Число от 1 до 10.