2011-10-22 160 views
2

我有一个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天数。

预期的结果集应该是:

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

我使用SQL Server 2008中因此,即使热膨胀系数,光标等都是可以接受的。

感谢
Raghu

回答

3

这使用master..spt_values作为adhoc辅助数字表。您可以使用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 
+0

+1 ....但aaaaah ...花了15分钟才能理解它,我不确定我能否以相同的方式重写它。 – xanatos

+0

我不会使用spt_values表,因为它没有证件性质。但是,与xanatos给出的CTE相比,确实是一个非常简单的查询。 – Raghu

+0

我会给你另外一个链接的+1,因为它非常有趣:-) – xanatos

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之间的天列表,删除多次出现并数着日子更天。

+0

此功能现在可用。不过,我已经在桌面上有大约10K条记录,并且需要8秒以上才能完成! BreakdownLog表将继续增长。现在,狩猎正在寻找一种技术来加快查询速度。 有任何建议,以使其受欢迎。 谢谢, Raghu – Raghu

+1

@Raghu如果修复时间始终<1027,您可以使用Martin Smith的解决方案,否则您可以预先生成一个数字表,如Martin Smith的链接 – xanatos

+0

中所建议的那样。 1027天是很多时间来修复一个设备!所以,我要用Martin Smith的解决方案;只有我将自己创建Numbers表而不是使用spt_values表。无论如何,感谢基于CTE的解决方案。 – Raghu