我有一个看起来像这样的表:
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
有可能吗?
答案 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中提供的eomonth
和iif
,但在旧版本中可以轻松替换它们
答案 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