1

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.

Mortalus
  • 10,574
  • 11
  • 67
  • 117

5 Answers5

2

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

Community
  • 1
  • 1
Matthew
  • 10,244
  • 5
  • 49
  • 104
2

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;

Example on SQL Fiddle


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;

Example on SQL Fiddle

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).

GarethD
  • 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
  • 1
    My 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
1

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)
rs.
  • 26,707
  • 12
  • 68
  • 90
  • 2
    Based on title wants +- 30 from – paparazzo May 02 '13 at 17:56
  • 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
1

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)

sgeddes
  • 62,311
  • 6
  • 61
  • 83
0

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'

Carboxyl
  • 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