2014-06-26 3 views
1

Мои данные выглядят ниже. enter image description hereКак я могу найти отсутствующий диапазон дат в sql server 2008?

Как найти недостающий диапазон дат из таблицы ss.

Я хочу найти недостающие (ss date range) диапазон дат между se_startdate и se_enddate.

например выше.

Пропущенные диапазоны дат являются

2014-07-01 to 2014-07-06 
2014-07-18 to 2014-07-30. 
+0

Я попытался с помощью зрения и используя время цикла, но не мог получить всю логику. – GPK

+0

Аналогичный вопрос: [SQL Query, чтобы показать пробелы между несколькими диапазонами дат] (https://stackoverflow.com/questions/9604400/sql-query-to-show-gaps-between-multiple-date-ranges) – Anssssss

+0

Может ли SS_StartDate быть раньше se_startdate или SS_EndDate будет позже se_enddate? Другими словами - интервал (se_startdate, se_enddate) всегда включает интервал (SS_StartDate, SS_EndDate)? – Alexander

ответ

2

Там может быть более простой способ сделать это, но часто при попытке найти недостающие номера/даты вам нужно создать эти цифры/даты затем LEFT JOIN в существующий данных, чтобы найти то, что отсутствует. Вы можете создать даты в вопросе с рекурсивным КТР:

WITH cal AS (SELECT CAST('2014-07-01' AS DATE) dt 
       UNION ALL 
       SELECT DATEADD(DAY,1,dt) 
       FROM cal 
       WHERE dt < '2014-07-30') 
SELECT * 
FROM cal 

Затем вы LEFT JOIN к столу, чтобы получить список отсутствующих дат:

WITH cal AS (SELECT CAST('2014-07-01' AS DATE) dt 
       UNION ALL 
       SELECT DATEADD(DAY,1,dt) 
       FROM cal 
       WHERE dt < '2014-07-30') 
SELECT DISTINCT cal.dt 
FROM cal 
LEFT JOIN YourTable a 
    ON cal.dt BETWEEN CAST(SS_StartDate AS DATE) AND CAST(SS_EndDate AS DATE) 
WHERE a.SS_StartDate IS NULL 

Тогда вам необходимо выяснить, является ли или нет последовательные строки принадлежат в том же диапазоне, или если они имеют зазор между ними, используя DATEDIFF() и ROW_NUMBER():

WITH cal AS (SELECT CAST('2014-07-01' AS DATE) dt 
       UNION ALL 
       SELECT DATEADD(DAY,1,dt) 
       FROM cal 
       WHERE dt < '2014-07-30') 
    ,dt_list AS (SELECT DISTINCT cal.dt 
        FROM cal 
        LEFT JOIN YourTable a 
        ON cal.dt BETWEEN CAST(SS_StartDate AS DATE) AND CAST(SS_EndDate AS DATE) 
        WHERE a.SS_StartDate IS NULL)   
SELECT dt 
     ,DATEDIFF(D, ROW_NUMBER() OVER(ORDER BY dt), dt) AS dt_range 
FROM dt_list 

Затем с помощью MIN() и MAX() получить диапазоны:

WITH cal AS (SELECT CAST('2014-07-01' AS DATE) dt 
       UNION ALL 
       SELECT DATEADD(DAY,1,dt) 
       FROM cal 
       WHERE dt < '2014-07-30') 
    ,dt_list AS (SELECT DISTINCT cal.dt 
        FROM cal 
        LEFT JOIN YourTable a 
        ON cal.dt BETWEEN CAST(SS_StartDate AS DATE) AND CAST(SS_EndDate AS DATE) 
        WHERE a.SS_StartDate IS NULL)   
    ,dt_range AS (SELECT dt 
         ,DATEDIFF(D, ROW_NUMBER() OVER(ORDER BY dt), dt) AS dt_range 
        FROM dt_list) 
SELECT MIN(dt) AS BeginRange 
     ,MAX(dt) AS EndRange 
FROM dt_range 
GROUP BY dt_range; 
--OPTION (MAXRECURSION 0) 

Демо: SQL Fiddle

Примечание: Если диапазон вы проверяете более чем 100 дней вам необходимо указать MAXRECURSION, 0 означает, что нет предела.

Примечание2: Если ваши SE дат предназначены для привода полного диапазона дат, а затем изменить cal КТР с фиксированных дат для запросов с использованием MIN() и MAX() соответственно.

+0

Спасибо. Это сработало для меня. – GPK

+2

Для чего стоит использовать рекурсивный cte для генерации последовательного ряда (http://sqlperformance.com/2013/01/t-sql-queries/generate-a-set-1), вероятно, является худшим способом сделать он (явные петли в сторону). Также в SQL Server ['NOT EXISTS' не выполнит' LEFT JOIN/IS NULL'] (http://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left-join -is-нуль-SQL-сервер /). Тем не менее, плюс 1 все еще по мере того, как логика находится на месте. – GarethD

+0

@GarethD Хорошие заметки, я использую «НЕ СУЩЕСТВУЕТ» в большинстве ситуаций, но не так часто, когда объясняю, как делать что-то другим. Сложенный метод cte в ссылке на последовательные серии очень умный, я обычно использую 'FROM master..spt_values ​​v1, master..spt_values ​​v2 ...', но я думаю, что я переключусь. –

0

Недопустимый диапазон должен начинаться с se_StartDate или ss_EndDate + 1. Аналогично, он должен заканчиваться либо se_EndDate, либо ss_StartDate-1. Перенесите диапазоны кандидатов и отмените наложения.

Преимущество этого метода заключается в том, что точность времени легко регулируется в часах или минутах или секундах без необходимости перечисления всех тактов.

SQL Fiddle Demo

SELECT DISTINCT 
    range_start, range_end, se_StartDate, se_EndDate 
FROM MyTable t1 
CROSS APPLY (
    SELECT se_StartDate range_start 
    UNION ALL 
    SELECT DATEADD(day,1,SS_EndDate) 
) rs 
CROSS APPLY (
    SELECT se_EndDate range_end 
    UNION ALL 
    SELECT DATEADD(day,-1,SS_StartDate) 
    FROM MyTable 
    WHERE 
    se_StartDate = t1.se_StartDate AND 
    se_EndDate = t1.se_EndDate AND 
    SS_StartDate > range_start 
) re 
WHERE NOT EXISTS (
    SELECT 1 
    FROM MyTable 
    WHERE 
    range_start < SS_EndDate AND 
    range_end > SS_StartDate 
) 
Смежные вопросы