I'm trying to figure out how to write a query that will return a table of 61 record that will list a date for each record from the current date.
-
1Wouldn't that be sixty-one records? :) – Dan J May 02 '13 at 17:46
-
True :) i have made an edit – Mortalus May 02 '13 at 17:53
-
1I don't understand. Please provide sample input and output data. – Bill May 02 '13 at 17:54
5 Answers
This is a useful function I use, taken from here:
Explode Dates Between Dates, check and adjust parameter
Just send it Date-30 and Date+30
CREATE FUNCTION [dbo].[ExplodeDates] (@startdate DATETIME, @enddate DATETIME)
RETURNS TABLE
AS
RETURN (
WITH
N0 AS (SELECT 1 AS n UNION ALL SELECT 1)
,N1 AS (SELECT 1 AS n FROM N0 t1, N0 t2)
,N2 AS (SELECT 1 AS n FROM N1 t1, N1 t2)
,N3 AS (SELECT 1 AS n FROM N2 t1, N2 t2)
,N4 AS (SELECT 1 AS n FROM N3 t1, N3 t2)
,N5 AS (SELECT 1 AS n FROM N4 t1, N4 t2)
,N6 AS (SELECT 1 AS n FROM N5 t1, N5 t2)
,nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS num FROM N6)
SELECT DATEADD(day, num-1, @startdate) AS thedate
FROM nums
WHERE num <= DATEDIFF(day, @startdate, @enddate) + 1
);
GO
If you don't want the function, you can also simply use it as a query, declaring
@startdate = @myDate - 30
and
@enddate = @myDate + 30
The simplest, and probably most efficient way in SQL-Server to get a list of 61 dates is to use the system table Master.dbo.spt_values:
SELECT [Date] = DATEADD(DAY, number - 30, CAST(CURRENT_TIMESTAMP AS DATE))
FROM Master..spt_values
WHERE Type = 'P'
AND Number <= 60;
EDIT
If you are concerned about using undocumented system tables then this will do the same thing (again with no looping)
WITH T AS
( SELECT Number = ROW_NUMBER() OVER(ORDER BY Object_ID)
FROM sys.all_objects
)
SELECT [Date] = DATEADD(DAY, number - 30, CAST(CURRENT_TIMESTAMP AS DATE))
FROM T
WHERE Number <= 60;
Extensive testing has been done here on the merits of various methods of generating sequences of numbers. My preferred option would always be your own table (e.g. dbo.numbers, or in this case a calendar table).

- 68,045
- 10
- 83
- 123
-
2+1, I think this is clever but shouldn't we shy away from depending on undocumented system tables? – Matthew May 02 '13 at 18:04
-
You're right, it is generally advised to stay away from undocumented system tables, but `Master..spt_values` is used all the time internally by SQL-server, and I believe it is only "undocumented" because it is for internal use and theoretically contains no information relevant to us, the user. For completeness I've added an alternative that still uses no looping – GarethD May 02 '13 at 18:22
-
That is the simplest solution but i can't use system tables because the user i have does not have permissions to do that. – Mortalus May 02 '13 at 18:24
-
1My suggestion would always be to have your own numbers table that contains a sequential list of numbers for exactly this purpose, it can be permissioned easily and is not in danger of disappearing in the next version of sql-server. If this is not an option and the table must be created on the fly then Matthew's answer is the next best solution. – GarethD May 02 '13 at 18:26
Try this
;with DateList As
(
select GETDATE() as DateCol
union all
select datecol + 1 from datelist
where DateDiff(d, getdate(),datecol+1) < 31 and DateCol + 1 > GETDATE()
union all
select datecol - 1 from datelist
where DateDiff(d, datecol-1, getdate()) < 31 and DateCol - 1 < GETDATE()
)
select CONVERT(varchar(15), DateCol, 101) DateCol from DateList
order by 1
OPTION (MAXRECURSION 0)
If you want to join other table
declare @t table (code varchar(10));
insert into @t
values ('a'), ('b')
;with DateList As
(
select GETDATE() as DateCol
union all
select datecol + 1 from datelist
where DateDiff(d, getdate(),datecol+1) < 31 and DateCol + 1 > GETDATE()
union all
select datecol - 1 from datelist
where DateDiff(d, datecol-1, getdate()) < 31 and DateCol - 1 < GETDATE()
)
select * from DateList, @t
OPTION (MAXRECURSION 0)

- 26,707
- 12
- 68
- 90
-
2
-
Although for 61 loops the performance impact will be negligable, using recusive CTE to generate sequential ranges is a poor technique best avoided. Other methods and their merits are discussed [here](http://www.sqlperformance.com/2013/01/t-sql-queries/generate-a-set-2) – GarethD May 02 '13 at 17:58
In my opinion, the best way to approach this is not to use recursive ctes, temp tables, or system tables, but rather to create and reuse a date lookup table. Create the lookup table once, and then you can use it as needed.
From there, it's really easy to generate a list of dates:
select *
from datelookup
where datefull >= dateadd(day,-30,convert(varchar(10), getDate(), 120))
and datefull <= dateadd(day,30,convert(varchar(10), getDate(), 120));
SQL Fiddle Demo (includes sample code to create such a table)

- 62,311
- 6
- 61
- 83
-
+1 having a calendar table can be very useful, but it's not always a viable solution. – Matthew May 02 '13 at 18:32
This T-SQL code will generate your table:
DECLARE @dates TABLE (date_item DATE)
DECLARE @day DATE = DATEADD(DAY, -30, N'2013-05-02')
WHILE @day <= DATEADD(DAY, 30, N'2013-05-02')
BEGIN
INSERT INTO @dates (date_item) SELECT @day
SET @day = DATEADD(DAY, 1, @day)
END
The result is in @dates
. Obviously you will need to set the desired value for the center date in place of N'2013-05-02'

- 11
- 1
- 2
-
3-1 SQL is a set based language and `WHILE` loops and `CURSORs` should be used only when necessary (not very often in my opinion, if at all). There are plenty of ways to [generate a sequential list](http://www.sqlperformance.com/2013/01/t-sql-queries/generate-a-set-2), and this is probably the worst. – GarethD May 02 '13 at 18:15
-
OK, not set based but still a correct answer and for 61 loops not going to bring the server down. +1 to get you back to even. – paparazzo May 02 '13 at 18:40
-
@Blam If 2 people gave you directions that both went to the same place but the second went an indirect way and was 61 miles further would you still thank the second person and say they were good directions, or would you tell them that there was a quicker way? My point being just because it is only 61 loops it does not mean we should be promoting bad practises. – GarethD May 02 '13 at 20:37
-
1@GarethD But this is not 61 miles further. It is a few milliseconds further and the most simple syntax presented. The question was not about performance. Not the best answer but I did not feel it deserved to be voted down. – paparazzo May 02 '13 at 20:54