SQL Server: перекрывающиеся даты в одной таблице - PullRequest
0 голосов
/ 29 декабря 2011
TableA (
    id int, 
    match1 char,
    match2 char,
    startdate datetime,
    enddate,
    status char
)

Пример данных:

 id match1  match2   startdate   enddate        Status
 1  AAA     BBB      2006-01-01  2007-01-01 
 2  AAA     BBB      2006-12-12  2008-01-01
 3  AAA     BBB      2008-01-01  2012-02-02
 4  AAA     BBB      2002-01-01  2004-01-01
 5  DDD     EEE      2009-01-01  2012-01-01
 6  DDD     EEE      2011-01-01  2020-01-01
 7  DDD     EEE      2013-01-01  2015-01-01
 8  DDD     EFG      2009-01-01  2012-01-01

Я должен заполнить status 'FAIL' в tableA, когда для совпадения match1, match2 даты - начало и конец - накладываются друг на друга,

Когда ID = 2, вступают в силу даты от 2006-12-12 до 2008-01-01, которые перекрываются ID = 1.Итак, ID = 2 получает 'FAIL'.То же самое верно и для ID = 6.

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

 id match1  match2    startdate    enddate        Status
 1  AAA     BBB      2006-01-01  2007-01-01       NULL
 2  AAA     BBB      2006-12-12  2008-01-01       FAIL
 3  AAA     BBB      2008-01-01  2012-02-02       NULL
 4  AAA     BBB      2002-01-01  2004-01-01       NULL    
 5  DDD     EEE      2009-01-01  2012-01-01       NULL
 6  DDD     EEE      2011-01-01  2020-01-01       FAIL
 7  DDD     EEE      2013-01-01  2015-01-01       NULL
 8  DDD     EFG      2009-01-01  2012-01-01       NULL (because It has different match2)

Кроме того, мы сохраняем первую запись для того же match1 и match2 и не выполняем новую входящую перекрытую запись.

Ответы [ 2 ]

2 голосов
/ 29 декабря 2011

Почему ID 7 не является Fail?Он пересекается (пересекается) с идентификатором 6?

ID7 не дает сбоя, поскольку ID6 не удался и исключен из нашего рассмотрения

Этот момент, который вы указали вкомментарии исключают возможность решения только для SQL, потому что ваше определение FAIL становится рекурсивным.Другими словами, для того, чтобы узнать, произошел ли сбой или нет, недостаточно знать дату start и end: вы также должны знать статус прохождения или сбоя «предыдущего поколения» записей.

Вот запрос, который может помочь вам определить первый перекрывающийся идентификатор, игнорируя правило "перекрытия с ошибочными записями не считаются":

select a.*,(
    select top 1 b.id from tableA b
    where a.match1=b.match1 and a.match2=b.match2
    and a.startdate>b.startdate
    and (case when a.startdate<b.startdate then b.startdate else a.startdate end) <
    (case when a.enddate>b.enddate then b.enddate else a.enddate end)
    order by b.startdate asc
) as OverlappingId
from tableA a

Возвращает эти результаты для данных из вашеготаблица:

1   AAA BBB 2006-01-01  2007-01-01  NULL
2   AAA BBB 2006-12-12  2008-01-01  1
3   AAA BBB 2008-01-01  2012-02-02  NULL
4   AAA BBB 2002-01-01  2004-01-01  NULL
5   DDD EEE 2009-01-01  2012-01-01  NULL
6   DDD EEE 2011-01-01  2020-01-01  5
7   DDD EEE 2013-01-01  2015-01-01  6
8   DDD EFG 2009-01-01  2012-01-01  NULL

Если вы должны обратить внимание на правило «перекрытия с ошибками», вам необходимо применять его последовательно;SQL не очень хорош в этом.

1 голос
/ 29 декабря 2011

Для этого вам нужен рекурсивный CTE или курсор.Рекурсивный подход CTE ниже.

;WITH BaseData(id,match1,match2,startdate,enddate)
     AS (SELECT 1,'AAA','BBB','2006-01-01','2007-01-01' UNION ALL
         SELECT 2,'AAA','BBB','2006-12-12','2008-01-01' UNION ALL
         SELECT 3,'AAA','BBB','2008-01-01','2012-02-02' UNION ALL
         SELECT 4,'AAA','BBB','2002-01-01','2004-01-01' UNION ALL
         SELECT 5,'DDD','EEE','2009-01-01','2012-01-01' UNION ALL
         SELECT 6,'DDD','EEE','2011-01-01','2020-01-01' UNION ALL
         SELECT 7,'DDD','EEE','2013-01-01','2015-01-01' UNION ALL
         SELECT 8,'DDD','EFG','2009-01-01','2012-01-01'    ),
     RecursiveCTE
     AS (SELECT id,
                match1,
                match2,
                startdate,
                enddate,
                CAST(NULL AS VARCHAR(4)) AS Status,
                enddate                  AS LastSuccessfulEnd
         FROM   (SELECT *,
                        ROW_NUMBER() OVER (PARTITION BY match1, match2 
                                               ORDER BY startdate) RN
                 FROM   BaseData) B
         WHERE  RN = 1
         UNION ALL
         SELECT id,
                match1,
                match2,
                startdate,
                enddate,
                Status,
                LastSuccessfulEnd
         FROM   (
                SELECT B.*,
                       rn = ROW_NUMBER() OVER (PARTITION BY B.match1, B.match2 
                                                   ORDER BY B.startdate),
                       CASE
                         WHEN B.startdate < R.LastSuccessfulEnd THEN 'FAIL'
                       END AS Status,
                       CASE
                         WHEN B.startdate >= R.LastSuccessfulEnd THEN B.enddate
                         ELSE R.enddate
                       END AS LastSuccessfulEnd
                 FROM   BaseData B
                        JOIN RecursiveCTE R
                          ON R.match1 = B.match1
                             AND R.match2 = B.match2
                             AND B.startdate > R.startdate) R
         WHERE  R.rn = 1)
SELECT id,
       match1,
       match2,
       startdate,
       enddate,
       Status
FROM   RecursiveCTE
ORDER  BY id 
...