Как вставить 1000 случайных дат между заданным диапазоном? - PullRequest
17 голосов
/ 10 марта 2012

Я новичок в SQL Server.Мне нужно генерировать случайные даты, выбранные из заданного диапазона дат.Как и дата найма работника должна быть где-то между 2011-01-01 и 2011-12-31.Сгенерированные даты должны быть вставлены в таблицу из 1000 строк случайным образом.

Может ли кто-нибудь направить меня с моим запросом?

Ответы [ 4 ]

38 голосов
/ 10 марта 2012
declare @FromDate date = '2011-01-01'
declare @ToDate date = '2011-12-31'

select dateadd(day, 
               rand(checksum(newid()))*(1+datediff(day, @FromDate, @ToDate)), 
               @FromDate)
6 голосов
/ 27 апреля 2018

Вы можете просто использовать этот запрос.

DATEADD(DAY, ABS(CHECKSUM(NEWID()) % 364 ), '2011-01-01')

Если вы хотите добавить даты из другой временной шкалы, вы можете изменить 01-01-2011 и 364.364 равно дням, которые вы хотите добавить.В данном случае это значение между 01-01-2011 и 31-12-2011.
(31-12-2011 также включено.)


Например, допустим, вы хотите добавить случайную дату между 2018-01-01 и 2018-01-31, вы можете изменить запрос следующим образом.

DATEADD(DAY, ABS(CHECKSUM(NEWID()) % 31), '2018-01-01')

Чтобы вставить (одну строку / дату), просто используйте это ...

DECLARE @rdate DATE = DATEADD(DAY, ABS(CHECKSUM(NEWID()) % 31), '2018-01-01')

INSERT INTO TableName ([DateColumn])
VALUES (@rdate);

Вывод

+-----+------------+
| ID  | DateColumn |
+-----+------------+
| 01  | 2018-01-21 |
+-----+------------+

Демонстрация в режиме онлайн: SQLFiddle.com


Чтобы вставить 1000 строк одновременно ...

DECLARE @rdate DATE
DECLARE @startLoopID INT = 1
DECLARE @endLoopID INT = 1000 -- Rows you want to add

WHILE @startLoopID <= @endLoopID
BEGIN
    SET @rdate = DATEADD(DAY, ABS(CHECKSUM(NEWID()) % 364 ), '2011-01-01');
    SET @startLoopID = @startLoopID + 1;

    INSERT INTO TableName ([DateColumn])
    VALUES (@rdate);
END

Вывод

+--------+------------+
|  ID    | DateColumn |
+--------+------------+
| 10000  | 2010-04-07 |
| 10001  | 2010-07-29 |
| 10002  | 2010-11-18 |
| 10003  | 2010-05-27 |
| 10004  | 2010-01-31 |
| 10005  | 2010-08-26 |
|   ˅    |     ˅      |
| 20000  | 2010-06-26 |
+--------+------------+

Демонстрация в режиме онлайн: SQLFiddle.com


Чтобы обновить существующие строки ...

UPDATE TableName
SET [DateColumn] = DATEADD(DAY, ABS(CHECKSUM(NEWID()) % 364 ), '2011-01-01')
WHERE condition;

Онлайн-демонстрация: SQLFiddle.com

5 голосов
/ 10 марта 2012

Я написал вам эту простую функцию, которая возвращает случайную дату в диапазоне дат:

create function date_rand ( @fromDate date, @toDate date) returns date
as
begin

 declare @days_between int
 declare @days_rand int

 set @days_between = datediff(day,@fromDate,@toDate)
 set @days_rand  = cast(RAND()*10000 as int)  % @days_between

 return dateadd( day, @days_rand, @fromDate )
end

для вызова функции:

select dbo.date_rand( '1/1/2001', '10/1/2001' )

вы можете комбинировать функцию со строкойгенератор:

;WITH Nbrs_3( n ) AS ( SELECT 1 UNION SELECT 0 ),
Nbrs_2( n ) AS ( SELECT 1 FROM Nbrs_3 n1 CROSS JOIN Nbrs_3 n2 ),
Nbrs_1( n ) AS ( SELECT 1 FROM Nbrs_2 n1 CROSS JOIN Nbrs_2 n2 ),
Nbrs_0( n ) AS ( SELECT 1 FROM Nbrs_1 n1 CROSS JOIN Nbrs_1 n2 ),
Nbrs ( n ) AS ( SELECT 1 FROM Nbrs_0 n1 CROSS JOIN Nbrs_0 n2 )
SELECT dbo.date_rand( '1/1/2001', '10/1/2001' )
FROM ( SELECT ROW_NUMBER() OVER (ORDER BY n)
FROM Nbrs ) D ( n )
WHERE n <= 1000 

EDITED

Для генерации случайных чисел используйте:

RAND(CHECKSUM(NEWID()))

вместо RAND ()

EDITED II

Функция возвращает «Недопустимое использование побочного эффекта оператора rand в функции».Это потому, что мы не можем использовать недетерминированные функции, такие как RAND () или NEWID ().

Обходной путь - создать представление как :

create view myRandomNumber as 
select cast( RAND(CHECKSUM(NEWID()))*1000 as int) as new_rand

и затем используйте его в функции:

...
select @days_rand  = new_rand  % @days_between from myRandomNumber
...

или просто не используйте функцию и пишите выражение при выборе.Я написал только функцию, объясняющую пошаговое решение.

declare @fromdate date
declare @todate date
set @fromdate = '1/1/2001'
set @todate = '10/1/2001'
;WITH Nbrs_3( n ) AS ( SELECT 1 UNION SELECT 0 ),
Nbrs_2( n ) AS ( SELECT 1 FROM Nbrs_3 n1 CROSS JOIN Nbrs_3 n2 ),
Nbrs_1( n ) AS ( SELECT 1 FROM Nbrs_2 n1 CROSS JOIN Nbrs_2 n2 ),
Nbrs_0( n ) AS ( SELECT 1 FROM Nbrs_1 n1 CROSS JOIN Nbrs_1 n2 ),
Nbrs ( n ) AS ( SELECT 1 FROM Nbrs_0 n1 CROSS JOIN Nbrs_0 n2 )
SELECT 
   dateadd( day, 
            cast( RAND(CHECKSUM(NEWID()))*1000 as int) % 
                         datediff(day,@fromDate,@toDate), 
            @fromDate )
FROM ( SELECT ROW_NUMBER() OVER (ORDER BY n)
FROM Nbrs ) D ( n )
WHERE n <= 1000 

Вы можете проверить здесь этот запрос .

0 голосов
/ 01 февраля 2016

Ну, я знаю, что это старый вопрос, но он был связан с более новым, так что ... Вот мои 2 цента:

  1. Таблицы базы данных по природе не отсортированы.
  2. В данном году есть только 365 возможных дат, 366, если это високосный год.
  3. Дублированные данные - признак плохого дизайна.

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

Сначала сохраните данные в таблице. вы можете использовать таблицу Tally для создания соответствующего диапазона дат.
Таблица Tally - это таблица, содержащая последовательность чисел. ради аргумента, давайте предположим, что вы уже создали свою таблицу чисел от 0 до 1 000 000.
Вы можете проверить эту ссылку для лучшего способа ее создания, лично мне нравится этот метод:

-- create the tally table
SELECT TOP 100000  IDENTITY (int ,0, 1) as num 
INTO Tally 
FROM sys.sysobjects 
CROSS JOIN sys.all_columns

Теперь, когда у вас есть таблица Tally, довольно просто создать календарь:

DECLARE @FromDate datetime = GETDATE(), 
        @ToDate datetime = DATEADD(YEAR, 1, GETDATE()) -- a year from now in my example

;With CalendarCTE AS
(
SELECT DATEADD(DAY, num, @FromDate) As caneldarDate 
FROM Tally
WHERE num < DATEDIFF(DAY, @FromDate, @ToDate) 
)

Теперь, когда у вас есть календарь и таблица подсчета, довольно просто использовать их обе для получения любого количества записей в любом порядке. Тысяча случайно упорядоченных дат? нет проблем:

SELECT TOP 1000 caneldarDate
FROM CalendarCTE c
CROSS JOIN Tally t 
WHERE t.num < 1000
ORDER BY NEWID()

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

-- create the tally table
SELECT TOP 100000  IDENTITY (int ,0, 1) as num 
INTO Tally 
FROM sys.sysobjects 
CROSS JOIN sys.all_columns

-- crealte the calendar cte:
DECLARE @FromDate datetime = GETDATE(), 
        @ToDate datetime = DATEADD(YEAR, 1, GETDATE())

;With CalendarCTE AS
(
SELECT DATEADD(DAY, num, @FromDate) As caneldarDate 
FROM Tally
WHERE num < DATEDIFF(DAY, @FromDate, @ToDate) 
)

-- select a 1000 random dates
SELECT TOP 1000 caneldarDate
FROM CalendarCTE c
CROSS JOIN Tally t 
WHERE t.num < 1000
ORDER BY NEWID()

-- cleanup
DROP TABLE Tally 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...