Как правильно применять рекурсивный CTE? - PullRequest
6 голосов
/ 14 мая 2019

У меня есть таблица, которая содержит посещения больниц для пациентов.Я пытаюсь пометить посещения, в которых посещения begin_date перекрывают предыдущие посещения end_date + 90 дней.Однако предостережение заключается в том, что если посещение помечается как посещение с перекрытием, то такое посещение не следует использовать для оценки совпадения с другим посещением.Позвольте мне объяснить на примере.

Таблица

visitID     patientid    begin_date  end_date
1           23           1/12/2018   1/14/2018
2           23           1/30/2018   2/14/2018
3           23           4/20/2018   4/22/2018
4           23           5/02/2018   5/03/2018
5           23           7/23/2018   7/28/2018

В приведенном выше примере пациент имел 5 посещений.Визит 2 begin_date был в диапазоне посещения 1 end_date + 90 дней, поэтому визит 2 должен быть отмечен.Как только посещение 2 помечено, эта строка не должна использоваться в анализе для любых будущих посещений.Концептуально это все равно, что удалить посещение 2 и снова начать анализ.

промежуточный этап (посещение 2 удалено, и анализ начинается снова)

visitID     patientid    begin_date  end_date
1           23           1/12/2018   1/14/2018
3           23           4/20/2018   4/22/2018
4           23           5/02/2018   5/03/2018
5           23           7/23/2018   7/28/2018

Так что, хотя посещение 3 совпадает спосещение 2, поскольку посещение 2 было удалено, посещение 3 не будет помечено, поскольку предыдущее посещение (теперь посещение 1) прошло более чем end_date + 90 дней от посещения 3 begin_date.Затем посещение 4 должно быть помечено, поскольку оно совпадает с посещением, которое не было отмечено (посещение 3).Таким образом, поскольку посещение 4 помечено, то посещение 5 будет удалено, поскольку оно begin_date находится в диапазоне посещения 3 с end_date + 90 дней.

Ожидаемый вывод

visitID     patientid    begin_date  end_date    flag
1           23           1/12/2018   1/14/2018   0
2           23           1/30/2018   2/14/2018   1
3           23           4/20/2018   4/22/2018   0
4           23           5/02/2018   5/03/2018   1
5           23           7/23/2018   7/28/2018   1

@ gordonlinoff ответил на очень похожий вопрос здесь , но у меня возникают проблемы с использованием рекурсивных CTE.Разница между вопросами заключается в том, что этот вопрос должен ссылаться на другой столбец (end_date), а не на один столбец даты.Рекурсивные CTE - все еще новая концепция для меня, но я надеюсь, что это поможет укрепить концепцию.

Моя попытка разгадать эту загадку (свиное отступление от @ gordonlinoff ):

with vt as (
          select vt.*, row_number() over (partition by patientid order by begin_date) as seqnum
          from visits_table vt
         ),
         cte as (
          select vt.visit, vt.patientid, vt.begin_date, vt.end_date, vt.begin_date as first_begin_date, seqnum
          from vt
          where seqnum = 1
          union all
          select vt.visit, vt.patientid, vt.begin_date, vt.end_date,
                 (case when vt.begin_date > dateadd(day, 90, cte.end_date) then vt.begin_date else cte.end_date end),
                 vt.seqnum
          from cte join
               vt
               on vt.seqnum = cte.seqnum + 1 and vt.patientid = cte.patientid
         )
    select cte.visit, cte.patientid, cte.begin_date, cte.end_date,
           (case when first_begin_date = begin_date then 0 else 1 end) as flag
    from cte
    order by cte.patientid, cte.begin_date;

Мои правки неправильно ссылаются на дату окончания в зависимости от результатов.Однако я не могу найти, где должно быть сравнение между begin_date и end_date.

Набор данных:

create table visits_table (visit int,patientid int,begin_date date, end_date date);

INSERT INTO visits_table (visit, patientid, begin_date, end_date) VALUES (1,23,'1/12/2018','1/14/2018')
INSERT INTO visits_table (visit, patientid, begin_date, end_date) VALUES (2,23,'1/30/2018','2/14/2018')
INSERT INTO visits_table (visit, patientid, begin_date, end_date) VALUES (3,23,'4/20/2018','4/22/2018')
INSERT INTO visits_table (visit, patientid, begin_date, end_date) VALUES (4,23,'5/02/2018','5/03/2018')

1 Ответ

1 голос
/ 14 мая 2019

Я настроил ваши данные выборки, чтобы сделать посещение 5 в диапазоне посещений 3's end_date + 90 дней. Дата окончания посещения 3 2018-04-22. Если мы добавим к нему 90 дней, это будет 2018-07-21. Ваши примеры данных в вопросе имеют дату начала посещения 5 как 2018-07-23, которая не пересекается с 2018-07-21. Поэтому я изменил это значение на 2018-07-20, чтобы эти даты перекрывались.

create table visits_table (visit int,patientid int,begin_date date, end_date date);

INSERT INTO visits_table (visit, patientid, begin_date, end_date) VALUES 
(1,23,'2018-01-12','2018-01-14'),
(2,23,'2018-01-30','2018-02-14'),
(3,23,'2018-04-20','2018-04-22'),
(4,23,'2018-05-02','2018-05-03'),
(5,23,'2018-07-20','2018-07-28');

Ваш запрос был довольно близок, вам просто нужно было рассчитать даты начала и окончания «предыдущего» интервала (first_begin_date, first_end_date).

Если «текущий» интервал перекрывается с «предыдущим», вы переносите «предыдущий» интервал в текущую строку.

Раскомментируйте строки в запросе ниже, чтобы увидеть все промежуточные значения.

with
vt
as
(
    select vt.*, row_number() over (partition by patientid order by begin_date) as seqnum
    from visits_table vt
)
,cte
as
(
    select
        vt.visit
        ,vt.patientid
        ,vt.begin_date as first_begin_date
        ,vt.end_date as first_end_date
        ,vt.begin_date
        ,vt.end_date
        ,seqnum
    from vt
    where seqnum = 1

    union all

    select
        vt.visit
        ,vt.patientid
        ,case when vt.begin_date <= dateadd(day, 90, cte.first_end_date)
            then cte.first_begin_date -- they overlap, keep the previous interval
            else vt.begin_date
        end as first_begin_date
        ,case when vt.begin_date <= dateadd(day, 90, cte.first_end_date)
            then cte.first_end_date -- they overlap, keep the previous interval
            else vt.end_date
        end as first_end_date
        ,vt.begin_date
        ,vt.end_date
        ,vt.seqnum
    from
        cte
        inner join vt
            on  vt.seqnum = cte.seqnum + 1
            and vt.patientid = cte.patientid
)
select
    cte.visit
    ,cte.patientid
    ,cte.begin_date
    ,cte.end_date
    ,case when first_begin_date = begin_date 
        then 0 else 1
    end as flag
--  ,DATEADD(day, 90, cte.end_date) AS enddd
--  ,*
from cte
order by cte.patientid, cte.begin_date;

Результат

+-------+-----------+------------+------------+------+
| visit | patientid | begin_date |  end_date  | flag |
+-------+-----------+------------+------------+------+
|     1 |        23 | 2018-01-12 | 2018-01-14 |    0 |
|     2 |        23 | 2018-01-30 | 2018-02-14 |    1 |
|     3 |        23 | 2018-04-20 | 2018-04-22 |    0 |
|     4 |        23 | 2018-05-02 | 2018-05-03 |    1 |
|     5 |        23 | 2018-07-20 | 2018-07-28 |    1 |
+-------+-----------+------------+------------+------+
...