如何正确应用递归 CTE?

How to properly apply recursive CTE?

我有一个 table,其中包含患者的医院就诊信息。我试图标记一次访问 begin_date 与之前的访问 end_date + 90 天重叠的访问。但是,需要注意的是,一旦访问被标记为重叠访问,则不应使用该访问来评估与另一次访问的重叠。让我用一个例子来解释。

Table

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 answered a very similar question ,但我 运行 遇到使用递归 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;

根据结果,我的编辑不正确地引用了 end_date。但是,我找不到 begin_dateend_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')

我调整了您的示例数据,使访问 5 在访问 3 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');

您的查询非常接近,您只需计算 "previous" 间隔 (first_begin_date, first_end_date).

的开始日期和结束日期

如果 "current" 间隔与 "previous" 重叠,则将 "previous" 间隔转移到当前行。

取消注释下面查询中的行以查看所有中间值。

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 |
+-------+-----------+------------+------------+------+