Фильтрация записей с указанием даты начала и окончания (сумма от 1 дня до даты в Oracle 12) - PullRequest
0 голосов
/ 07 сентября 2018

СЦЕНАРИЙ

  • есть таблица CONTRACT со ссылкой на клиента
  • каждый клиент может иметь более одного контракта
  • контракты всегда открываются (VALID_FROM) и иногда они закрываются (заполняется VALID_TO)

Для каждого клиента я хочу отказаться от всех контрактов с продолжением (один контракт закрыт, а другой открывается на следующий день - для того же клиента)

Упрощение структуры стола

CONTRACTS
    - ID (PK INTEGER)
    - CUSTOMER_ID (FK INTEGER NOT NULL)
    - VALID_FROM (DATE NOT NULL)
    - VALID_TO (DATE NULLABLE)

ПРИМЕРНЫЕ ДАННЫЕ

ID|CUSTOMER_ID|VALID_FROM|VALID_TO
1|1|2018-01-01|2018-07-31
2|1|2018-11-01|NULL
3|2|2018-03-01|2018-04-30
4|2|2018-05-01|2018-11-30
5|3|2018-06-01|NULL

ОЖИДАЕМЫЙ РЕЗУЛЬТАТ

ID|CUSTOMER_ID|VALID_FROM|VALID_TO
1|1|2018-01-01|2018-07-31
2|1|2018-11-01|NULL
4|2|2018-05-01|2018-11-30
5|3|2018-06-01|NULL

SQL

SELECT
  C.*
FROM CONTRACTS C
LEFT JOIN CONTRACTS C1 ON (C.CUSTOMER_ID=C1.CUSTOMER_ID AND *C.VALID_TO + 1 DAY*=C1.VALID_FROM)
WHERE C1.ID IS NULL

Вопрос

Мне нужно изменить C.VALID_TO + 1 ДЕНЬ , каков правильный синтаксис в Oracle?

1 Ответ

0 голосов
/ 07 сентября 2018

Вы можете явно указать, что добавляете дни, используя тип интервала;

LEFT JOIN CONTRACTS C1
ON (C.CUSTOMER_ID=C1.CUSTOMER_ID AND C.VALID_TO + INTERVAL '1' DAY=C1.VALID_FROM)

или более просто используйте арифметику даты , просто удалив слово 'DAY' из вашего запроса:

SELECT
  C.*
FROM CONTRACTS C
LEFT JOIN CONTRACTS C1 ON (C.CUSTOMER_ID=C1.CUSTOMER_ID AND C.VALID_TO + 1=C1.VALID_FROM)
WHERE C1.ID IS NULL
ORDER BY C.CUSTOMER_ID, C.VALID_FROM;

        ID CUSTOMER_ID VALID_FROM VALID_TO  
---------- ----------- ---------- ----------
         1           1 2018-01-01 2018-07-31
         2           1 2018-11-01           
         4           2 2018-05-01 2018-11-30
         5           3 2018-06-01           

В качестве бонуса два альтернативных подхода; вместо использования левого соединения используйте not exists:

SELECT
  C.*
FROM CONTRACTS C
WHERE NOT EXISTS (
  SELECT *
  FROM CONTRACTS C1
  WHERE C.CUSTOMER_ID=C1.CUSTOMER_ID AND C.VALID_TO + 1=C1.VALID_FROM
)
ORDER BY C.CUSTOMER_ID, C.VALID_FROM;

или используйте встроенное представление и аналитический вызов lead(), чтобы вам приходилось нажимать на стол только один раз:

SELECT ID, CUSTOMER_ID, VALID_FROM, VALID_TO
FROM (
  SELECT
    C.*,
    LEAD(VALID_FROM) OVER (PARTITION BY CUSTOMER_ID ORDER BY VALID_FROM) AS LEAD_VALID_FROM
  FROM CONTRACTS C
)
WHERE LEAD_VALID_FROM IS NULL OR VALID_TO + 1 != LEAD_VALID_FROM
ORDER BY CUSTOMER_ID, VALID_FROM;

Оба получают одинаковые результаты с вашими данными выборки.

db <> fiddle demo для всех четырех запросов. .

...