Как создать случайное число для каждой строки в 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:
-
CAST()
: https://docs.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql -
RAND()
: http://msdn.microsoft.com/en-us/library/ms177610.aspx -
CHECKSUM()
: http://msdn.microsoft.com/en-us/library/ms189788.aspx -
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
Ответ 11
Вам нужно будет вызвать RAND() для каждой строки. Вот хороший пример
https://web.archive.org/web/20090216200320/http://dotnet.org.za/calmyourself/archive/2007/04/13/sql-rand-trap-same-value-per-row.aspx
Ответ 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.