tags:

views:

74

answers:

5

Possible Duplicate:
Getting Dates between a range of dates

Let's say I have 2 dates (date part only, no time) and I want to get all dates between these 2 dates inclusive and insert them in a table. Is there an easy way to do it with a SQL statement (i.e without looping)?

Ex:
Date1: 2010-12-01
Date2: 2010-12-04

Table should have following dates:
2010-12-01, 2010-12-02, 2010-12-03, 2010-12-04
+4  A: 

Assuming SQL Server 2005+, use a recursive query:

WITH sample AS (
  SELECT CAST('2010-12-01' AS DATETIME) AS dt
  UNION ALL
  SELECT DATEADD(dd, 1, dt)
    FROM sample s
   WHERE DATEADD(dd, 1, dt) <= CAST('2010-12-04' AS DATETIME))
SELECT * 
  FROM sample

Returns:

 dt
 ---------
 2010-12-01 00:00:00.000
 2010-12-02 00:00:00.000
 2010-12-03 00:00:00.000
 2010-12-04 00:00:00.000

Use CAST/CONVERT to format as you like.

Using parameters for start & end:

INSERT INTO dbo.YOUR_TABLE
  (datetime_column)
WITH sample AS (
    SELECT @start_date AS dt
    UNION ALL
    SELECT DATEADD(dd, 1, dt)
      FROM sample s
     WHERE DATEADD(dd, 1, dt) <= @end_date)
SELECT s.dt
  FROM sample s
OMG Ponies
See this answer for [performance benchmarks](http://stackoverflow.com/questions/10819/sql-auxiliary-table-of-numbers/2663232#2663232) of recursive CTEs.
Martin Smith
@Martin Smith: Interesting - KM and I compared about a year back, found the subtreecost for the example put the CTE barely ahead of the NUMBERS trick.
OMG Ponies
@OMG - I found the same results as Jeff Moden when doing my own testing and looking at the actual cpu, reads, and duration. Doing `SELECT COUNT(*) FROM <Recursive CTE returning 1 million numbers>` took massively longer than doing `SELECT COUNT(*) FROM <Cross Joined CTE returning 1 million numbers>`
Martin Smith
`;WITH L0 AS (SELECT 1 AS c UNION ALL SELECT 1),L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B),L5 AS (SELECT 1 AS c FROM L4 A CROSS JOIN L4 B),Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS n FROM L5) SELECT COUNT(n) FROM Nums WHERE n<=1000000` takes less than 1 second.
Martin Smith
`;WITH Nums AS (SELECT 1 AS n UNION ALL SELECT n+1 FROM Nums WHERE n < 1000000) SELECT COUNT(n) FROM Nums WHERE n<=1000000 OPTION (MAXRECURSION 0);` takes > 20 seconds on my desktop.
Martin Smith
@Martin Smith: The recursive one took 15 seconds on our Dev box, waiting to see if the co-lo staff will call me about it =)
OMG Ponies
+1  A: 

You need a numbers table. If you don't have a permanent one this is a more efficient way of generating one than using a recursive CTE. A permanent one will be more efficient though as long as it is read from the buffer cache.

DECLARE @D1 DATE = '2010-12-01'
DECLARE @D2 DATE = '2010-12-04'

;WITH 
L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),
L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),
L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B),
Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS i FROM L4)
SELECT DATEADD(day,i-1,@D1)
 FROM Nums where i <= 1+DATEDIFF(day,@D1,@D2)
Martin Smith
A: 

Repeated Question

http://stackoverflow.com/questions/271595/getting-dates-between-a-range-of-dates

DECLARE @DateFrom smalldatetime, @DateTo smalldatetime;
SET @DateFrom='20000101';
SET @DateTo='20081231';
-------------------------------
WITH T(date)
AS
( 
SELECT @DateFrom 
UNION ALL
SELECT DateAdd(day,1,T.date) FROM T WHERE T.date < @DateTo
)
SELECT date FROM T OPTION (MAXRECURSION 32767);
SDReyes
TSQL is SQL Server or Sybase, no Oracle/PLSQL supplied in there (that I saw) but Oracle didn't support the recursive WITH until 11gR2.
OMG Ponies
Thanks! (editing...) btw they offer many solutions one of them is Oracle oriented :) +1
SDReyes
A: 

I just did something like this:

declare @dt datetime = '2010-12-01'
declare @dtEnd datetime = '2010-12-04'

WHILE (@dt < @dtEnd) BEGIN
    insert into table(datefield)
        values(@dt)
    SET @dt = DATEADD(day, 1, @dt)
END
Chris Lively
@Chris Lively - Thanks, but one of the requirements was not to use a loop (see original question).
dcp
A: 

I think this post has what you are looking to do: http://sqlserver.posterous.com/t-sql-tuesday-return-a-range-of-dates-between

Antony Highsky