Get a list of dates between two dates using a function

前端 未结 21 1077
天涯浪人
天涯浪人 2020-11-22 06:25

My question is similar to this MySQL question, but intended for SQL Server:

Is there a function or a query that will return a list of days between two dates? For exa

相关标签:
21条回答
  • 2020-11-22 06:57

    Try something like this:

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

    You then use:

    SELECT *
    FROM dbo.ExplodeDates('20090401','20090531') as d;
    

    Edited (after the acceptance):

    Please note... if you already have a sufficiently large nums table then you should use:

    CREATE FUNCTION dbo.ExplodeDates(@startdate datetime, @enddate datetime)
    returns table as
    return (
    SELECT DATEADD(day,num-1,@startdate) as thedate
    FROM nums
    WHERE num <= DATEDIFF(day,@startdate,@enddate) + 1
    );
    

    And you can create such a table using:

    CREATE TABLE dbo.nums (num int PRIMARY KEY);
    INSERT dbo.nums values (1);
    GO
    INSERT dbo.nums SELECT num + (SELECT COUNT(*) FROM nums) FROM nums
    GO 20
    

    These lines will create a table of numbers containing 1M rows... and far quicker than inserting them one by one.

    You should NOT create your ExplodeDates function using a function that involves BEGIN and END, as the Query Optimizer becomes unable to simplify the query at all.

    0 讨论(0)
  • 2020-11-22 06:58

    if you're in a situation like me where procedures and functions are prohibited, and your sql user does not have permissions for insert, therefore insert not allowed, also "set/declare temporary variables like @c is not allowed", but you want to generate a list of dates in a specific period, say current year to do some aggregation, use this

    select * from 
    (select affffdate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) gen_date from
     (select 0 t0 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
     (select 0 t1 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
     (select 0 t2 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
     (select 0 t3 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
     (select 0 t4 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
    where gen_date between '2017-01-01' and '2017-12-31'
    
    0 讨论(0)
  • 2020-11-22 06:59

    This does exactly what you want, modified from Will's earlier post. No need for helper tables or loops.

    WITH date_range (calc_date) AS (
        SELECT DATEADD(DAY, DATEDIFF(DAY, 0, '2010-01-13') - DATEDIFF(DAY, '2010-01-01', '2010-01-13'), 0)
            UNION ALL SELECT DATEADD(DAY, 1, calc_date)
                FROM date_range
                WHERE DATEADD(DAY, 1, calc_date) <= '2010-01-13')
    SELECT calc_date
    FROM date_range;
    
    0 讨论(0)
  • 2020-11-22 07:00

    A Bit late to the party, but I like this solution quite a bit.

    CREATE FUNCTION ExplodeDates(@startDate DateTime, @endDate DateTime)
    RETURNS table as
    return (
        SELECT  TOP (DATEDIFF(DAY, @startDate, @endDate) + 1)
                DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @startDate) AS DATE
        FROM    sys.all_objects a
                CROSS JOIN sys.all_objects b
                )
    
    0 讨论(0)
  • 2020-11-22 07:02

    this few lines are the simple answer for this question in sql server.

    WITH mycte AS
    (
      SELECT CAST('2011-01-01' AS DATETIME) DateValue
      UNION ALL
      SELECT  DateValue + 1
      FROM    mycte   
      WHERE   DateValue + 1 < '2021-12-31'
    )
    
    SELECT  DateValue
    FROM    mycte
    OPTION (MAXRECURSION 0)
    
    0 讨论(0)
  • 2020-11-22 07:02
    DECLARE @StartDate DATE = '2017-09-13',         @EndDate DATE = '2017-09-16'
    
    SELECT date  FROM (   SELECT DATE = DATEADD(DAY, rn - 1, @StartDate)   FROM    (
        SELECT TOP (DATEDIFF(DAY, @StartDate, DATEADD(DAY,1,@EndDate)))
          rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
        FROM sys.all_objects AS s1
        CROSS JOIN sys.all_objects AS s2
        ORDER BY s1.[object_id]   ) AS x ) AS y
    

    Result:

    2017-09-13
    
    2017-09-14
    
    2017-09-15
    
    2017-09-16
    
    0 讨论(0)
提交回复
热议问题