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