假设我有一个表From
和To
列是日期和一个位类型列,用于标识是否为取消(1 =取消)。还有一个Id
,它是PK和CancelId
,它引用了被取消的内容。
假设我的记录如下:
Id From To IsCancel CancelId
1 2015-01-01 2015-01-31 0 NULL
2 2015-01-03 2015-01-09 1 1
3 2015-01-27 2015-01-31 1 1
我希望结果能够显示当前非取消记录的间隔时间仍然没有取消:
Id From To
1 2015-01-01 2015-01-02
1 2015-01-10 2015-01-26
我可以这样做,它会将每条记录分成日期,然后从记录中减去取消的日期,然后合并间隔,但由于我有很多记录,我发现这非常低效,我很确定我可以忽略简单的事情。
答案 0 :(得分:1)
您想要实现的任务非常重要。可能的解决方案是将所有 From / To 日期放在有序序列中。以下UNPIVOT
操作:
SELECT ID, EventDate, StartStop,
ROW_NUMBER() OVER (ORDER BY ID, EventDate, StartStop) AS EventRowNum,
IsCancel
FROM
(SELECT ID, IsCancel, [From], [To]
FROM Event) Src
UNPIVOT (
EventDate FOR StartStop IN ([From], [To])
) AS Unpvt
生成此结果集:
ID EventDate StartStop EventRowNum IsCancel
--------------------------------------------------
1 2015-01-01 From 1 0
2 2015-01-03 From 2 1
2 2015-01-09 To 3 1
3 2015-01-27 From 4 1
3 2015-01-31 To 5 1
1 2015-01-31 To 6 0
使用CTE
,您可以随后模拟LEAD
函数(从SQL Server 2012开始提供),以便将上述序列中的当前和下一个日期放在单个记录中:
;WITH StretchEventDates AS
(
-- above query goes here
), CTE AS
(
SELECT s.ID, s.EventDate, s.StartStop, s.IsCancel,
sLead.EventDate As LeadEventDate, sLead.StartStop AS LeadStartStop, sLead.IsCancel AS LeadIsCancel
FROM StretchEventDates AS s
LEFT JOIN StretchEventDates AS sLead ON s.EventRowNum + 1 = sLead.EventRowNum
)
以上产生以下结果集:
ID EventDate StartStop IsCancel LeadEventDate LeadStartStop LeadIsCancel
--------------------------------------------------------------------------------------
1 2015-01-01 From 0 2015-01-03 From 1
2 2015-01-03 From 1 2015-01-09 To 1
2 2015-01-09 To 1 2015-01-27 From 1
3 2015-01-27 From 1 2015-01-31 To 1
3 2015-01-31 To 1 2015-01-31 To 0
1 2015-01-31 To 0 NULL NULL NULL
使用CASE
语句,您可以过滤这些记录以获得所需的输出。
全部放在一起:
;WITH StretchEventDates AS
(
SELECT ID, EventDate, StartStop,
ROW_NUMBER() OVER (ORDER BY EventDate, StartStop) AS EventRowNum,
IsCancel
FROM
(SELECT ID, IsCancel, [From], [To]
FROM Event) Src
UNPIVOT (
EventDate FOR StartStop IN ([From], [To])
) AS Unpvt
), CTE AS
(
SELECT s.ID, s.EventDate, s.StartStop, s.IsCancel,
sLead.EventDate As LeadEventDate, sLead.StartStop AS LeadStartStop, sLead.IsCancel AS LeadIsCancel
FROM StretchEventDates AS s
LEFT JOIN StretchEventDates AS sLead ON s.EventRowNum + 1 = sLead.EventRowNum
), CTE_FINAL AS
(SELECT *,
CASE WHEN StartStop = 'From' AND IsCancel = 0 THEN EventDate
WHEN StartStop = 'To' AND IsCancel = 1 THEN DATEADD(d, 1, EventDate)
END AS [From],
CASE WHEN LeadStartStop = 'From' AND LeadIsCancel = 1 THEN DATEADD(d, -1, LeadEventDate)
WHEN LeadStartStop = 'To' AND LeadIsCancel = 0 THEN LeadEventDate
END AS [To]
FROM CTE
)
SELECT ID, [From], [To]
FROM CTE_FINAL
WHERE [From] IS NOT NULL AND [To] IS NOT NULL AND [From] <= [To]
您可能需要在上面的查询中添加额外的CASEs
来处理“取消”的其他组合&#39;以下&#39;未取消&#39; (反之亦然)事件。
使用OP中提供的数据,上面产生以下输出:
ID From To
---------------------------
1 2015-01-01 2015-01-02
2 2015-01-10 2015-01-26