Поиск промежутков между диапазонами дат, охватывающими записи - PullRequest
1 голос
/ 30 марта 2020

Я пытаюсь написать запрос, в котором я могу найти любой пробел в диапазонах дат для данного ID при передаче двух дат.

РЕДАКТИРОВАТЬ: мне нужно знать, если в моем диапазоне дат существует полный или частичный разрыв.

У меня есть данные в этом формате:

Example 1:

| ID | START_DATE | END_DATE   |
|----|------------|------------|
| 1  | 01/01/2019 | 30/09/2019 |
| 1  | 01/03/2020 | (null)     |

Example 2:

| ID | START_DATE | END_DATE   |
|----|------------|------------|
| 2  | 01/01/2019 | 30/09/2019 |
| 2  | 01/10/2019 | 01/12/2019 |
| 2  | 02/12/2019 | (null)     |

NB. Нулевая конечная дата по существу означает «все еще активна до текущего дня».

Например, Example 1 имеет разрыв в 152 дня между 30/09/2019 и 01.032020. Если я запросил в диапазоне 05/05/2019 - 01/09/2019, в этом диапазоне нет пробела. Принимая во внимание, что если я смотрю на диапазон дат 05/05/2019 - 02/10/2019, то в этом диапазоне есть один дневной разрыв.

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

Я пытался сделать что-то подобное, но это не работает, когда моя дата попадает в промежуток:

SELECT SUM(START_DATE - PREV_END - 1)
FROM
   (
   SELECT ID, START_DATE, END_DATE, LAG(END_DATE) OVER (ORDER BY START_DATE) AS PREV_END_DATE
   FROM TBL
   WHERE ID = X_ID
   )
WHERE START_DATE >= Y_FIRST_DATE
AND START_DATE <= Z_SECOND_DATE;

X_ID, Y_FIRST_DATE и Z_SECOND_DATE это просто какой-то другой идентификатор или диапазон дат, которые я мог бы указать.

Как я мог go об этом?

Ответы [ 4 ]

1 голос
/ 30 марта 2020

Это еще одна разновидность проблемы островков и пробелов, которая часто появляется здесь. Я думаю, что это соответствует функциональности сопоставления с шаблоном Oracle. Возьмите этот пример:

WITH tbl AS
(
  SELECT 1 AS ID, to_date('01/01/2019', 'DD/MM/YYYY') AS START_DATE, to_date('30/09/2019', 'DD/MM/YYYY') AS END_DATE FROM DUAL
  UNION ALL
  SELECT 1 AS ID, to_date('01/03/2020', 'DD/MM/YYYY') AS START_DATE, NULL AS END_DATE FROM DUAL
  UNION ALL
  SELECT 2 AS ID, to_date('01/01/2019', 'DD/MM/YYYY') AS START_DATE, to_date('30/09/2019', 'DD/MM/YYYY') AS END_DATE FROM DUAL
  UNION ALL
  SELECT 2 AS ID, to_date('01/10/2019', 'DD/MM/YYYY') AS START_DATE, to_date('01/12/2019', 'DD/MM/YYYY') AS END_DATE FROM DUAL
  UNION ALL
  SELECT 2 AS ID, to_date('02/12/2019', 'DD/MM/YYYY') AS START_DATE, NULL AS END_DATE FROM DUAL
)
SELECT *
FROM tbl
MATCH_RECOGNIZE(ORDER BY ID, start_date
                MEASURES b.id AS ID,
                         a.end_date+1 AS GAP_START,
                         b.start_date-1 AS GAP_END
                PATTERN (A B+)
                DEFINE B AS start_date > PREV(end_date)+1 AND ID = PREV(ID))L;

Я знаю, что это выглядит долго, но большая часть этого создает предложение WITH. Сопоставление с образцом позволяет вам определить, что такое пробел, и соответственно извлекать информацию. Обратите внимание, что для того, чтобы пробел был, ваша начальная дата должна быть больше предыдущей конечной даты + 1, сгруппированной по столбцу ID.

Чтобы улучшить это, чтобы ответить на ваш обновленный / отредактированный вопрос, просто добавьте эту строку код до конца:

WHERE GREATEST(gap_start, TO_DATE('15/09/2019', 'DD/MM/YYYY' /*Y_FIRST_DATE*/)) <= LEAST(gap_end, to_date('15/10/2019', 'DD/MM/YYYY')/*Z_SECOND_DATE*/)
1 голос
/ 30 марта 2020

Другим вариантом определения дней может быть использование синтаксиса SELECT .. FROM dual CONNECT BY LEVEL <= через EXIST пробелов с помощью INTERSECT из двух наборов: один находит все даты между параметрами экстремума, а другой находит все даты, соответствующие датам. вставлены в таблицу в виде границ:

SELECT CASE WHEN 
       SUM( 1 + LEAST(Z_SECOND_DATE,NVL(END_DATE,TRUNC(SYSDATE))) 
         - GREATEST(Y_FIRST_DATE,START_DATE) ) = Z_SECOND_DATE - Y_FIRST_DATE + 1 THEN
           'NO Gap'
       ELSE
           'Gap Exists'  
       END "gap?"
  FROM TBL t
 WHERE ID = X_ID
   AND EXISTS ( SELECT Y_FIRST_DATE + LEVEL - 1
                  FROM dual
               CONNECT BY LEVEL <= Z_SECOND_DATE - Y_FIRST_DATE + 1 
               INTERSECT
                SELECT t.START_DATE + LEVEL - 1
                  FROM dual
               CONNECT BY LEVEL <= NVL(t.END_DATE,TRUNC(SYSDATE))- t.START_DATE + 1
               )    

START_DATE значения считаются ненулевыми на основании данных выборки.

Демонстрация

0 голосов
/ 30 марта 2020

Я бы предложил найти максимальную дату окончания до текущей записи - на основе даты начала.

Это будет:

select t.*
from (select t.*,
             max(end_date) over (order by start_date
                                 rows between unbounded preceding and 1 preceding
                                ) as max_prev_end_date
      from tbl t
      where start_date <= :input_end_date and
            end_date >= :input_start_date
     ) t
where max_prev_end_date < start_date;
0 голосов
/ 30 марта 2020

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

SELECT
    CASE WHEN SUM(CASE WHEN T.ID IS NULL THEN 1 END) > 0 
         THEN 'THERE IS GAP'
         ELSE 'THERE IS NO GAP'
    END AS RESULT_
FROM ( SELECT P_IN_FROM_DATE + LEVEL - 1 AS CUST_DATES
         FROM DUAL
        CONNECT BY LEVEL <= P_IN_TO_DATE - P_IN_FROM_DATE + 1
    ) CUST_TBL
    LEFT JOIN TBL T 
    ON CUST_TBL.CUST_DATES BETWEEN T.START_DATE AND T.END_DATE
       OR ( CUST_TBL.CUST_DATES >= T.START_DATE AND T.END_DATE IS NULL )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...