Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 将日期时间拆分为个月SQL Server_Sql Server_Datetime - Fatal编程技术网

Sql server 将日期时间拆分为个月SQL Server

Sql server 将日期时间拆分为个月SQL Server,sql-server,datetime,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-0

我有一张这样的桌子:

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
有可能吗?

使用递归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中提供的eomonth和iif,但它们可以在较旧的版本中轻松替换

请查看下面的查询,如果我遗漏了什么,请告诉我

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

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

创建一个函数,返回给定开始和结束日期的月份开始日期。你的答案也很好,但我决定采用Ildenny的一个。。。因为这是我第一次尝试。谢谢,呵呵。美好的但是您添加了一些额外的计算。我的错误是,查询返回的日期比DateTo列中需要的时间少了1个月。所以,您应该只在计算列中添加1个月,而不涉及递归:更新了我的query@Uzi:如果比较两个执行计划和io统计信息,您会发现查询更复杂。按名称划分的maxstep很昂贵,需要额外扫描。此外,除oemonth外,我的版本与任何版本的SQLS都兼容。最后,从风格上来说,我更喜欢最终选择的清洁剂,而不是CTE。
-- 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