日期范围交叉点

时间:2011-10-22 10:26:30

标签: 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-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为NULL,我们假设设备仍然不存在,因此计算Defunct days到当前日期。

预期结果集应为:

EquipmentID|DefunctDays
1          |122
2          |122
3          |83
4          |103
5          |143

我正在使用SQL Server 2008.所以即使是CTE,游标等也是可以接受的。

感谢
Raghu

2 个答案:

答案 0 :(得分:3)

这使用master..spt_values作为特殊辅助数字表。您可以使用one of the techniques here创建自己的专用号码表(从零开始编号)

;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 :(得分:2)

应该是这样的:

测试表初始化

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

我们本可以更改最后两个选项:

SELECT EquipmentID, COUNT(DISTINCT DefunctDay) DefunctDays 
    FROM BaseDays
    GROUP BY EquipmentID

简化我使用递归CTE生成BreakdownDate和IssueFixedDate之间的天数列表,删除多次出现的天数并计算天数。

相关问题