Tsql 日期范围交点
我有一个包含以下列的BreakdownLog表:Tsql 日期范围交点,tsql,common-table-expression,date-range,Tsql,Common Table Expression,Date Range,我有一个包含以下列的BreakdownLog表: EquipmentID, ProblemID, BreakdownDate, IssueFixedDate 每个设备可以有多个故障,更重要的是,同一设备的日期范围可能重叠 例如,给出以下数据: EquipmentID|ProblemID|BreakdownDate|IssueFixedDate 1 |1 |01-Jun-2011 |01-Sep-2011 1 |2 |01-Jun
EquipmentID, ProblemID, BreakdownDate, IssueFixedDate
每个设备可以有多个故障,更重要的是,同一设备的日期范围可能重叠
例如,给出以下数据:
EquipmentID|ProblemID|BreakdownDate|IssueFixedDate
1 |1 |01-Jun-2011 |01-Sep-2011
1 |2 |01-Jun-2011 |01-Oct-2011
2 |1 |01-Jun-2011 |01-Oct-2011
2 |2 |01-Jun-2011 |01-Oct-2011
3 |1 |15-Jun-2011 |01-Sep-2011
3 |2 |10-Jun-2011 |25-Aug-2011
4 |1 |01-Jun-2011 |01-Aug-2011
4 |2 |10-Sep-2011 |22-Oct-2011
5 |1 |01-Jun-2011 |15-Jun-2011
5 |2 |02-Jun-2011 |NULL
现在我需要一个查询,可以计算出每台设备失效的天数。如果IssueFixedDate为空,则我们假设设备仍然失效,因此计算截至当前日期的失效天数
预期结果集应为:
EquipmentID|DefunctDays
1 |122
2 |122
3 |83
4 |103
5 |143
我正在使用SQLServer2008。因此,即使是CTE、光标等也是可以接受的
谢谢
Raghu应该是这样的: 测试表初始化
DROP TABLE BreakdownLog
CREATE TABLE BreakdownLog
(
EquipmentID INT,
ProblemID INT,
BreakdownDate DATETIME,
IssueFixedDate DATETIME NULL
);
INSERT INTO BreakDownLog VALUES (1, 1, '01-Jun-2011', '01-Sep-2011')
INSERT INTO BreakDownLog VALUES (1, 2, '01-Jun-2011', '01-Oct-2011')
INSERT INTO BreakDownLog VALUES (2, 1, '01-Jun-2011', '01-Oct-2011')
INSERT INTO BreakDownLog VALUES (2, 2, '01-Jun-2011', '01-Oct-2011')
INSERT INTO BreakDownLog VALUES (3, 1, '15-Jun-2011', '01-Sep-2011')
INSERT INTO BreakDownLog VALUES (3, 2, '10-Jun-2011', '25-Aug-2011')
INSERT INTO BreakDownLog VALUES (4, 1, '01-Jun-2011', '01-Aug-2011')
INSERT INTO BreakDownLog VALUES (4, 2, '10-Sep-2011', '22-Oct-2011')
INSERT INTO BreakDownLog VALUES (5, 1, '01-Jun-2011', '15-Jun-2011')
INSERT INTO BreakDownLog VALUES (5, 2, '02-Jun-2011', NULL)
实码
-- We exchange the NULLs in IssueFixedDate with the current date
; WITH Base AS (
SELECT EquipmentID, ProblemID, BreakdownDate
, ISNULL(IssueFixedDate
, CONVERT(VARCHAR(10), GETDATE(), 101)) IssueFixedDate
-- The previous line generates the current date without time
FROM BreakDownLog
)
-- We generate a table with all the days the equipment was broken.
-- This is done through a recursive CTE
, BaseDays AS (
SELECT EquipmentID, BreakdownDate AS DefunctDay, IssueFixedDate FROM Base
UNION ALL
SELECT EquipmentID, DefunctDay + 1 AS DefunctDay, IssueFixedDate
FROM BaseDays
WHERE DefunctDay + 1 <= IssueFixedDate
-- In T-SQL if you add 1 to a DateTime it's equivalent to adding a day
)
-- We make a distinct on the days where the equipment was broken,
-- to delete days where the equipment was broken for two reasons
, BaseDaysDistinct AS (
SELECT DISTINCT EquipmentID, DefunctDay
FROM BaseDays
)
-- We group the equipment's DefunctDays by EquipmentID
SELECT EquipmentID, COUNT(*) DefunctDays
FROM BaseDaysDistinct
GROUP BY EquipmentID
简化我正在使用递归CTE生成BreakdownDate和IssueFixedDate之间的天数列表,删除出现多次的天数并计算天数。这使用master..spt_值作为临时辅助数字表。您可以使用“从零开始编号”创建自己的专用编号表
;WITH Numbers
AS (SELECT number
FROM master..spt_values
WHERE type = 'P')
SELECT EquipmentID,
COUNT(DISTINCT number + DATEDIFF(DAY,0, BreakdownDate)) - 1 AS DefunctDays
FROM BreakdownLog
JOIN Numbers N
ON number <= DATEDIFF(DAY, BreakdownDate,
ISNULL(IssueFixedDate, GETDATE()))
GROUP BY EquipmentID
+1.... 但是啊。。。我花了15分钟来理解它,我不确定我是否能够以同样的方式重写它。我不会使用spt_值表,因为它没有文档记录。但与xanatos给出的CTE相比,确实是一个非常简单的查询。我会给你另一个+1的链接,因为它非常有趣:-尽管如此,与CTE版本相比,这是非常快的。我正在考虑自己创建一个数字表,而不是使用spt_值@Raghu如果你看一下例子中的基准测试,你会发现伊兹克的交叉连接CTE非常便宜,如果你只需要几千个数字的话。然而,我已经有大约10公里的记录在表中,它需要8秒以上完成!BreakdownLog表将继续增长。现在,我们正在寻找一种可以加快查询速度的技术。欢迎提出更多建议。谢谢Raghu@Raghu如果修复时间始终小于1027,您可以使用Martin Smith的解决方案,否则您可以像Martin Smith的链接中建议的那样预生成一个数字表。您是对的。1027天是修复设备的很长时间!所以,我将使用马丁·史密斯的解;只是我要自己创建一个数字表,而不是使用spt_值表。无论如何,感谢基于CTE的解决方案。
;WITH Numbers
AS (SELECT number
FROM master..spt_values
WHERE type = 'P')
SELECT EquipmentID,
COUNT(DISTINCT number + DATEDIFF(DAY,0, BreakdownDate)) - 1 AS DefunctDays
FROM BreakdownLog
JOIN Numbers N
ON number <= DATEDIFF(DAY, BreakdownDate,
ISNULL(IssueFixedDate, GETDATE()))
GROUP BY EquipmentID