Tsql 日期范围交点

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

我有一个包含以下列的BreakdownLog表:

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