How to Generate a Temporary Table Filled with Dates in SQL Server 2000

How can I generate a temporary table filled with dates in SQL Server 2000?

This will quickly populate a table with 170 years worth of dates.

CREATE TABLE CalendarMonths (
date DATETIME,
PRIMARY KEY (date)
)

DECLARE
@basedate DATETIME,
@offset INT
SELECT
@basedate = '01 Jan 2000',
@offset = 1

WHILE (@offset < 2048)
BEGIN
INSERT INTO CalendarMonths SELECT DATEADD(MONTH, @offset, date) FROM CalendarMonths
SELECT @offset = @offset + @offset
END

You can then use it by LEFT joining on to that table, for the range of dates you require.

Easiest way to populate a temp table with dates between and including 2 date parameters

This works even if the @StartDate is not the first of the month. I'm assuming that if it's not the start of the month, you want to begin with the first of the next month. Otherwise remove the +1.:

;WITH cte AS (
SELECT CASE WHEN DATEPART(Day,@StartDate) = 1 THEN @StartDate
ELSE DATEADD(Month,DATEDIFF(Month,0,@StartDate)+1,0) END AS myDate
UNION ALL
SELECT DATEADD(Month,1,myDate)
FROM cte
WHERE DATEADD(Month,1,myDate) <= @EndDate
)
SELECT myDate
FROM cte
OPTION (MAXRECURSION 0)

How to generate a range of dates in SQL Server

I would argue that for this specific purpose the below query is about as efficient as using a dedicated lookup table.

DECLARE @start DATE, @end DATE;
SELECT @start = '20110714', @end = '20110717';

;WITH n AS
(
SELECT TOP (DATEDIFF(DAY, @start, @end) + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects
)
SELECT 'Bob', DATEADD(DAY, n-1, @start)
FROM n;

Results:

Bob     2011-07-14
Bob 2011-07-15
Bob 2011-07-16
Bob 2011-07-17

Presumably you'll need this as a set, not for a single member, so here is a way to adapt this technique:

DECLARE @t TABLE
(
Member NVARCHAR(32),
RegistrationDate DATE,
CheckoutDate DATE
);

INSERT @t SELECT N'Bob', '20110714', '20110717'
UNION ALL SELECT N'Sam', '20110712', '20110715'
UNION ALL SELECT N'Jim', '20110716', '20110719';

;WITH [range](d,s) AS
(
SELECT DATEDIFF(DAY, MIN(RegistrationDate), MAX(CheckoutDate))+1,
MIN(RegistrationDate)
FROM @t -- WHERE ?
),
n(d) AS
(
SELECT DATEADD(DAY, n-1, (SELECT MIN(s) FROM [range]))
FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects) AS s(n)
WHERE n <= (SELECT MAX(d) FROM [range])
)
SELECT t.Member, n.d
FROM n CROSS JOIN @t AS t
WHERE n.d BETWEEN t.RegistrationDate AND t.CheckoutDate;
----------^^^^^^^ not many cases where I'd advocate between!

Results:

Member    d
-------- ----------
Bob 2011-07-14
Bob 2011-07-15
Bob 2011-07-16
Bob 2011-07-17
Sam 2011-07-12
Sam 2011-07-13
Sam 2011-07-14
Sam 2011-07-15
Jim 2011-07-16
Jim 2011-07-17
Jim 2011-07-18
Jim 2011-07-19

As @Dems pointed out, this could be simplified to:

;WITH natural AS 
(
SELECT ROW_NUMBER() OVER (ORDER BY [object_id]) - 1 AS val
FROM sys.all_objects
)
SELECT t.Member, d = DATEADD(DAY, natural.val, t.RegistrationDate)
FROM @t AS t INNER JOIN natural
ON natural.val <= DATEDIFF(DAY, t.RegistrationDate, t.CheckoutDate);

Generate a resultset of incrementing dates in TSQL

If your dates are no more than 2047 days apart:

declare @dt datetime, @dtEnd datetime
set @dt = getdate()
set @dtEnd = dateadd(day, 100, @dt)

select dateadd(day, number, @dt)
from
(select number from master.dbo.spt_values
where [type] = 'P'
) n
where dateadd(day, number, @dt) < @dtEnd

I updated my answer after several requests to do so. Why?

The original answer contained the subquery

 select distinct number from master.dbo.spt_values
where name is null

which delivers the same result, as I tested them on SQL Server 2008, 2012, and 2016.

However, as I tried to analyze the code that MSSQL internally when querying from spt_values, I found that the SELECT statements always contain the clause WHERE [type]='[magic code]'.

Therefore I decided that although the query returns the correct result, it delivers the correct result for wrong reasons:

There may be a future version of SQL Server which defines a different [type] value which also has NULL as values for [name], outside the range of 0-2047, or even non-contiguous, in which case the result would be simply wrong.

How to populate a table with a range of dates?

Try this:

DROP PROCEDURE IF EXISTS filldates;
DELIMITER |
CREATE PROCEDURE filldates(dateStart DATE, dateEnd DATE)
BEGIN
WHILE dateStart <= dateEnd DO
INSERT INTO tablename (_date) VALUES (dateStart);
SET dateStart = date_add(dateStart, INTERVAL 1 DAY);
END WHILE;
END;
|
DELIMITER ;
CALL filldates('2011-01-01','2011-12-31');

Here's the SQL Fiddle to play with it: http://sqlfiddle.com/#!2/65d13/1

EDIT (to check if date already exists) as asked by Andrew Fox.

CREATE PROCEDURE filldates(dateStart DATE, dateEnd DATE)

BEGIN

DECLARE adate date;

WHILE dateStart <= dateEnd DO

SET adate = (SELECT mydate FROM MyDates WHERE mydate = dateStart);

IF adate IS NULL THEN BEGIN

INSERT INTO MyDates (mydate) VALUES (dateStart);

END; END IF;

SET dateStart = date_add(dateStart, INTERVAL 1 DAY);

END WHILE;

END;//

Here's the SQL Fiddle to play with it: http://sqlfiddle.com/#!2/66f86/1

How can I get a table of dates from the first day of the month, two months ago, to yesterday?

This just does sequential days between two dates, but I've posted to show you can eliminate the recursive error by supplying a limit.

with temp (level, seqdate) as 
(select 1, date('2008-01-01')
from sysibm.sysdummy1
union all
select level, seqdate + level days
from temp
where level < 1000
and seqdate + 1 days < Date('2008-02-01')
)
select seqdate as CalendarDay
from temp
order by seqdate

Update from pax:

This answer actually put me on the right track. You can get rid of the warning by introducing a variable that's limited by a constant. The query above didn't have it quite right (and got the dates wrong, which I'll forgive) but, since it pointed me to the problem solution, it wins the prize.

The code below was the final working version (sans warning):

WITH DATERANGE(LEVEL,DT) AS (
SELECT 1, CURRENT DATE + (1 - DAY(CURRENT DATE)) DAYS - 2 MONTHS
FROM SYSIBM.SYSDUMMY1
UNION ALL SELECT LEVEL + 1, DT + 1 DAY
FROM DATERANGE
WHERE LEVEL < 1000 AND DT < CURRENT DATE - 1 DAY
) SELECT DT FROM DATERANGE;

which outputs, when run on the 2nd of February:

----------
DT
----------
2009-12-01
2009-12-02
2009-12-03
: : : :
2010-01-30
2010-01-31
2010-02-01

DSNE610I NUMBER OF ROWS DISPLAYED IS 63
DSNE616I STATEMENT EXECUTION WAS SUCCESSFUL.


Related Topics



Leave a reply



Submit