Во-первых, вы должны знать, что month не является точной единицей времени. Здесь я использовал функцию Oracle months_between
, но вы также можете вычесть даты и сравнить их с 30. Months_between
может дать результаты, которые не являются интуитивными, но они верны. Например:
select months_between(date '2019-03-29', date '2019-02-28') from dual;
select months_between(date '2019-03-31', date '2019-02-28') from dual;
Первый выбор дает 1.03
, второй дает 1
. Странно, но логично. Это потому, что месяц не является точной единицей.
Вы предупреждены :) Теперь решение. Сначала мои образцы данных, 3 различных номера политики с различными случаями:
create table policies(policy_no, casenumber, created_date) as (
select 1, 101, date '2007-01-01' from dual union all
select 1, 102, date '2007-02-01' from dual union all
select 1, 103, date '2007-06-01' from dual union all
select 1, 104, date '2007-09-15' from dual union all
select 1, 105, date '2007-11-01' from dual union all
select 1, 106, date '2007-12-01' from dual union all
select 2, 201, date '1992-08-30' from dual union all
select 3, 301, date '1995-07-12' from dual union all
select 3, 302, date '1995-08-30' from dual union all
select 3, 303, date '1997-02-25' from dual );
И мой запрос:
with
t(pn, cn, cdt, rn) as (
select policy_no, casenumber, created_date,
row_number() over (partition by policy_no order by created_date desc)
from policies),
c(pn, cn, cdt, rn, diff, ldt, info) as (
select pn, cn, cdt, 1, 0, cdt, 'last' from t where rn = 1
union all
select t.pn, t.cn, t.cdt, t.rn, round(months_between(c.ldt, t.cdt), 2),
case when months_between(c.ldt, t.cdt) >= 3 then t.cdt else c.ldt end,
case when months_between(c.ldt, t.cdt) >= 3 then 'inlcuded' else 'excluded' end
from c join t on t.pn = c.pn and t.rn = c.rn + 1)
select * from c order by pn, rn
Результат:
PN CN CDT RN DIFF LDT INFO
---------- ---------- ----------- ---------- ---------- ----------- --------
1 106 2007-12-01 1 0 2007-12-01 last
1 105 2007-11-01 2 1 2007-12-01 excluded
1 104 2007-09-15 3 2,55 2007-12-01 excluded
1 103 2007-06-01 4 6 2007-06-01 inlcuded
1 102 2007-02-01 5 4 2007-02-01 inlcuded
1 101 2007-01-01 6 1 2007-02-01 excluded
2 201 1992-08-30 1 0 1992-08-30 last
3 303 1997-02-25 1 0 1997-02-25 last
3 302 1995-08-30 2 17,84 1995-08-30 inlcuded
3 301 1995-07-12 3 1,58 1995-08-30 excluded
Вас интересуют только строки с информацией last
или included
.
Как это работает? Подзапрос t
только добавляет нумерацию к строкам, она для каждой политики разделяется, самый новый случай - первый. Подзапрос c
является основной частью решения.
Это рекурсивно. Мы начинаем с номеров строк 1
, и на каждом следующем шаге мы ищем следующий номер строки и проверяем, не старше ли его дата, чем три месяца после запоминания.
Если это так, мы сохраняем его (в столбце ldt), если нет, используется предыдущий.
Вот так работает рекурсивный запрос. Надеюсь, я правильно понял. Если вам нужно проверять только между соседними строками, тогда функции lag
или lead
будет достаточно, но здесь вам нужна рекурсия.
Надеюсь, это поможет и извините за любые языковые ошибки:)