简体   繁体   中英

Find the start and end date of two different dates in SQL Server, Group by month

If input is provided like below

declare @from datetime = '2016-09-15'
declare @to datetime = '2016-12-25'

Then output should be like below

Month     Start_date  End_date
September 2016-09-15  2016-09-30
October   2016-10-01  2016-10-31
November  2016-11-01  2016-11-30
December  2016-12-01  2016-12-25

If input is provided like this:

declare @from datetime = '2016-12-05'
declare @to datetime = '2016-12-25'

Then output should be like this:

Month     Start_date   End_date
December  2016-12-05   2016-12-25

Thanks in advance.

Try this, I hope this helps.

 DECLARE @fromDate DATE = '2016-09-05', @toDate DATE = '2016-12-25', @tempStartDate DATE

    DECLARE @tempTable TABLE(Month NVARCHAR(50), Start_Date DATE, End_Date DATE)

    SELECT @tempStartDate = @fromDate

    WHILE(CAST(@tempStartDate AS DATE) <= CAST(@toDate AS DATE))
    BEGIN

        INSERT INTO @tempTable 
            SELECT  DATENAME(MONTH, @tempStartDate),@tempStartDate, 
            CASE WHEN DATEPART(MONTH,@tempStartDate) = DATEPART(MONTH,@toDate) THEN @toDate ELSE DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,@tempStartDate)+1,0)) END
        SET @tempStartDate = DATEADD(s,1,DATEADD(mm, DATEDIFF(m,0,@tempStartDate)+1,0))

    END

    SELECT * FROM @tempTable

You can do this with an ad-hoc tally table. Using spt_values, but any larger table will do.

Furthermore, this approach would be faster than a recursive cte, especially for larger sets

declare @from date = '2016-09-15'
declare @to date   = '2016-12-25'

Select Month      = max(DateName(MONTH,D))
      ,Start_date = min(D)
      ,End_date   = max(D)
 From (Select Top (DateDiff(DD,@from,@to)+1) D=DateAdd(DD,Row_Number() Over (Order By (Select null))-1,@from) From master..spt_values ) A
 Group By Year(D),Month(D)
 Order By min(d)

Returns

Month       Start_date  End_date
September   2016-09-15  2016-09-30
October     2016-10-01  2016-10-31
November    2016-11-01  2016-11-30
December    2016-12-01  2016-12-25

Edit - As Requested

The process is really not that complicated. We use Row_Number() in concert with DateAdd() to generate a list of dates within the specified date range.

The sub-query produces the following

D
2016-09-15
2016-09-16
2016-09-17
2016-09-18
2016-09-19
2016-09-20
2016-09-21
2016-09-22
2016-09-23
...
2016-12-20
2016-12-21
2016-12-22
2016-12-23
2016-12-24
2016-12-25

Then it becomes a simple matter of getting the min/max dates by year/month.

Again, I used master..spt_values , but virtually any table would do. If you don't have a tally/numbers table, I would strongly recommend one.

It can be accomplished with a recursive CTE

declare @from datetime = '2016-09-15'
declare @to datetime = '2016-12-25'

;with cte_r
    AS
    (
    SELECT
            @from AS Dte1
            ,@to AS Dte2
            ,DATEADD(MM,DATEDIFF(MM,0,@from),0) AS MonthStrt
            ,DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,0,@from)+1,0)) AS MonthEnd
    UNION ALL
    SELECT
            @from AS Dte1
            ,@to AS Dte2
            ,DATEADD(MM,1,MonthStrt)
            ,DATEADD(MM,1,MonthEnd)
    FROM
            cte_r
    WHERE
        MonthEnd <=  @to
    )
SELECT          
    CASE WHEN MonthStrt <= @from THEN @from ELSE MonthStrt END AS Stat_Date
    ,CASE WHEN MonthEnd >= @to THEN @to ELSE MonthEnd END AS End_Date
FROM
    cte_r
declare @from date = '2016-09-15'

declare @to date   = '2016-12-25';
with cte as
(select datename(month,@from )as month1,1 as n , @from as startdate,eomonth(@from)as endate
union all 
select datename(month,dateadd(day,n,@from)),n+1,dateadd(day,n,@from),eomonth(dateadd(day,n,@from))
from cte where dateadd(day,n,@from)<@to
)select month1,min(startdate),max(endate) from cte
group by month1
order by 1 desc

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM