Выберите случайную дату в определенном диапазоне

Как я могу выбрать случайную дату в определенном инклюзивном диапазоне, скажем, "1950-01-01" и "1999-12-31" с SQL Server?

Ответ 1

Это даст вам 1000 строк данных для вставки.

DECLARE @D1 DATE = '19500101'
DECLARE @D2 DATE = '19991231'

   ;WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1),
        E02(N) AS (SELECT 1 FROM E00 a, E00 b),
        E04(N) AS (SELECT 1 FROM E02 a, E02 b),
        E08(N) AS (SELECT 1 FROM E04 a, E04 b),
        E16(N) AS (SELECT 1 FROM E08 a, E08 b),
        E32(N) AS (SELECT 1 FROM E16 a, E16 b),
   cteTally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY N) FROM E32)
   SELECT TOP 1000
    DATEADD(DAY,ABS(CHECKSUM(NEWID())) % (1+DATEDIFF(DAY,@D1,@D2)),@D1)
   FROM  cteTally

NB: этот ответ первоначально использовал ABS(CAST(CRYPT_GEN_RANDOM(4) AS INT)) для генерации случайных чисел. В отличие от RAND(), который оценивается только один раз за оператор, он оценивается один раз в строке, поэтому он будет работать.

Однако, похоже, оптимизатор запросов не понимает этого и рассматривает его как константу. Для генерации случайных данных это, вероятно, не имеет значения (если вы не заполняете столбец, ограниченный внешним ключом)

но я просто протестировал альтернативу ABS(CHECKSUM(NEWID())), чтобы увидеть, есть ли какое-либо преимущество в производительности одного над другим.

Типичные скорости для генерации 1000 000 строк с использованием таблицы чисел выше и выберите значение MAX (чтобы избежать накладных расходов на возврат всех этих строк клиенту)

ABS(CAST(CRYPT_GEN_RANDOM(4) AS INT))
/*CPU time = 4180 ms,  elapsed time = 4395 ms.*/

ABS(CHECKSUM(NEWID()))
/*CPU time = 953 ms,  elapsed time = 1163 ms.*/

(SELECT 1) /*A constant value just to get a baseline*/
/*CPU time = 499 ms,  elapsed time = 457 ms.*/

Поэтому, если вам не нужен криптографически безопасный PRNG, лучше избегать его!

Ответ 2

select DateAdd(d, ROUND(DateDiff(d, '1950-01-01', '1999-12-31') * RAND(), 0), '1950-01-01')

ИЗМЕНИТЬ

Если это нужно выполнить как часть оператора, возвращающего несколько строк или как часть обновления, RAND() вернет одно значение для всего набора результатов. В этом случае можно использовать RAND (CHECKSUM (NEWID()).

select DateAdd(d, ROUND(DateDiff(d, '1950-01-01', '1999-12-31') * RAND(), 0), '1950-01-01'),
       DateAdd(d, ROUND(DateDiff(d, '1950-01-01', '1999-12-31') * RAND(CHECKSUM(NEWID())), 0), '1950-01-01')
from master..spt_values where type = 'P'