Пересечения диапазона дат - PullRequest
       17

Пересечения диапазона дат

2 голосов
/ 22 октября 2011

У меня есть таблица 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, мы предполагаем, что оборудование по-прежнему не работает, и, следовательно, вычисляем количество неиспользованных дней до текущей даты.

Ожидаемый набор результатов должен быть:

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

Я использую SQL Server 2008. Поэтому допустимы даже CTE, курсоры и т. Д.

Спасибо
Рагу

Ответы [ 2 ]

3 голосов
/ 22 октября 2011

Используется master..spt_values как таблица вспомогательных номеров adhoc.Вы можете создать свою собственную таблицу выделенных номеров, используя один из методов здесь (начать нумерацию с нуля)

;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  
2 голосов
/ 22 октября 2011

Это должно быть что-то вроде:

Инициализация тестовой таблицы

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

Упрощение Я создаю список дней между BreakdownDate и IssueFixedDate, используя рекурсивный CTE, удаляя дни, которые появляются более одного раза, и считая дни.

...