将日期时间拆分为几个月的SQL Server

时间:2018-02-01 11:06:55

标签: sql-server datetime

我有一个看起来像这样的表:

Name |     DateFrom     |      DateTo
 A   | 2017-01-04 10:50 | 2017-03-05 18:20
 B   | 2017-01-31 23:00 | 2017-02-03 10:00

我希望将每个日期分成每月一行,如下所示:

Name |     DateFrom     |      DateTo
 A   | 2017-01-04 10:50 | 2017-02-01 00:00
 A   | 2017-02-01 00:00 | 2017-03-01 00:00
 A   | 2017-03-01 00:00 | 2017-03-05 18:20
 B   | 2017-01-31 23:00 | 2017-02-01 00:00  
 B   | 2017-02-01 00:00 | 2017-02-03 10:00

有可能吗?

4 个答案:

答案 0 :(得分:0)

使用recursive CTE来实现

with cte (Name, DateFrom, DateTo) as (
    select 
        a, cast(b as datetime), cast(c as datetime) 
    from (values 
        ('A', '20170104 10:50', '20170305 18:20')
         ,('B', '20170131 23:00', '20170203 10:00')
    ) t (a, b, c)
)
, rcte as (
    select 
        Name, res = cast(dateadd(dd, 1, eomonth(DateFrom, -1)) as datetime), DateFrom, DateTo, 1 step       
    from cte
    union all
    select
        name, dateadd(mm, 1, res), DateFrom, DateTo, step + 1
    from
        rcte
    where
        dateadd(mm, 1, res) < DateTo

)

select
    name, iif(step = 1, DateFrom, res)
    , iif(step = max(step) over (partition by Name), DateTo, dateadd(mm, 1, res))
from 
    rcte

我使用了SQL 2012中提供的eomonthiif,但在旧版本中可以轻松替换它们

答案 1 :(得分:0)

请查看以下查询,如果我遗漏了,请告诉我。

DECLARE @tblDates AS Table
(
    Name VARCHAR(10),
    DateFrom Datetime,
    DateTo Datetime
)

INSERT INTO @tblDates VALUES('A','2017-01-04 10:50','2017-03-05 18:20')
INSERT INTO @tblDates VALUES('B','2017-01-31 23:00','2017-02-03 10:00')

SELECT
    *,
    ROW_NUMBER() OVER(ORDER BY Name) AS RowNo
INTO #tblDates
FROM @tblDates  

DECLARE @startdate Datetime
DECLARE @enddate Datetime
DECLARE @rowIndex INT=1, @totalCount INT,@Name VARCHAR(10)

SELECT @totalCount=COUNT(*) FROM #tblDates

DECLARE @tempDate Datetime

DECLARE @finalDates AS TABLE(
    Name VARCHAR(10),
    DateFrom Datetime,
    DateTo Datetime
)

WHILE @rowIndex<=@totalCount
BEGIN
    SELECT @Name=Name,@startdate=DateFrom,@tempDate=DateFrom,@enddate=DateTo FROM #tblDates WHERE RowNo=@rowIndex
    ---SELECT 1,@tempDate
    WHILE @tempDate<=@enddate
    BEGIN       
        SET @startdate=DATEADD(m,1,@tempDate)
        ---SELECT @tempDate,@startdate,@enddate
        INSERT INTO @finalDates VALUES(@Name,@tempDate,
            CASE WHEN @startdate>@enddate 
            THEN CASE WHEN MONTH(@endDate)=MONTH(DATEADD(month, DATEDIFF(month, 0, @startdate), 0)) THEN DATEADD(month, DATEDIFF(month, 0, @enddate), 0) ELSE @enddate END
            ELSE DATEADD(month, DATEDIFF(month, 0, @startdate), 0) END)
        SET @tempDate=DATEADD(m,1,@tempDate)
        SET @tempDate=DATEADD(month, DATEDIFF(month, 0, @tempDate), 0)
    END
    SET @rowIndex=@rowIndex+1
END

SELECT * FROM @finalDates

DROP TABLE #tblDates

答案 2 :(得分:0)

Uzi的答案并未返回预期值。我拿了他的剧本并改了。

with cte (Name, DateFrom, DateTo) as (
    select 
        a, cast(b as datetime), cast(c as datetime) 
    from (values 
          ('A', '20170104 10:50', '20170305 18:20')
         ,('B', '20170131 23:00', '20170203 10:00')
         ,('C', '20170130 23:00', '20170131 10:00')
         ,('D', '20170331 23:00', '20170330 10:00')
    ) t (a, b, c)
)
, rcte as (
    select 
        Name, DateFrom, (CASE WHEN Cast(EOMONTH(DateFrom, 0) as Date) >= Cast(DateTo as date) THEN DateTo ELSE DATEADD(d, 1, EOMONTH(DateFrom, 0)) END) DateTo, DateFrom OriginalDateFrom, DateTo OriginalDateTo, 1 step       
    from cte
    union all
    select 
        Name, rcte.DateTo, (CASE WHEN Cast(EOMONTH(rcte.DateTo, 0) as Date) >= Cast(rcte.OriginalDateTo as Date) THEN rcte.OriginalDateTo ELSE DATEADD(d, 1, EOMONTH(rcte.DateTo, 0)) END), rcte.OriginalDateFrom, rcte.OriginalDateTo, rcte.step + 1    
    from rcte
    where 
        Cast(EOMONTH(DateFrom, 0) as Date) < Cast(rcte.OriginalDateTo as Date)          
)
select 
    Name, DateFrom, DateTo, Step
from 
    rcte
ORDER BY Name, DateFrom

答案 3 :(得分:0)

-- Create Table Calender (one time work)
--create using any logic
create table tblCalender
(
 DATEVal datetime not null primary key
)

insert into tblCalender
SELECT DATEADD(MONTH, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), '19000101')
FROM master..spt_values;

-- your table with millions of data
create table  #t (Name varchar(50),DateFrom datetime, DateTo datetime)
insert into #t VALUES
 ('A','2017-01-04 10:50','2017-03-05 18:20')
 ,('B','2017-01-31 23:00','2017-02-03 10:00')

 create nonclustered index #tFromTo on #t(DateFrom,DateTo)include(name)

 --if you use below 2012 then use  convert(varchar(6), getdate(),112)

SELECT h.NAME
    ,CASE 
        WHEN format(tc.DATEVal, 'yyyyMM') = format(DateFrom, 'yyyyMM')
            THEN DateFrom
        ELSE DATEVal
        END DateFrom
    ,CASE 
        WHEN format(tc.DATEVal, 'yyyyMM') = format(DateTo, 'yyyyMM')
            THEN DateTo
        ELSE dateadd(month, 1, DATEVal)
        END DateTo
FROM #t h
INNER JOIN tblCalender tc ON tc.DATEVal > DATEADD(MONTH, - 1, h.DateFrom)
    AND tc.DATEVal < h.DateTo


    drop table #t