如何正确应用递归CTE?

时间:2019-05-13 22:10:52

标签: sql sql-server recursion common-table-expression

我有一个表格,其中包含患者的医院就诊情况。我正在尝试标记访问次数printPdf: function (url) { //var htmlstring="<iframe onload=\"window.print()\" id=\"thisiframe\" name=\"thisiframe\"src=\"" + url + "\">" + "<script type=\"text/javascript\"> <\/script> <\/iframe>" var iframe1= document.createElement('iframe') iframe1.name='iframe1' iframe1.id='iframe1' iframe1.src=url var myscript1= iframe1.contentWindow.document.createElement('script') myscript1.type='text/javascript' myscript1.src='<scr' + 'ipt type="text/javascript"> printframe= function () {window.print()}; parent.tunnel(printframe()); </scr' + 'ipt>' iframe1.contentWindow.document.body.appendChild(myscript1) var iframe = document.createElement('iframe'); iframe.name='iframe' iframe.id='iframe' var myscript = iframe.contentWindow.document.createElement('script'); myscript.type = 'text/javascript'; myscript.src = '<scr' + 'ipt type="text/javascript"> function tunnel(fn) {fn(); document.body.removeChild(iframe);} </scr' + 'ipt>' iframe.contentWindow.document.body.appendChild(myscript) iframe.contentWindow.document.body.appendChild(iframe1) document.body.appendChild(iframe); }, 与前次访问次数begin_date + 90天重叠的访问次数。但是,需要注意的是,一旦将某个访问标记为重复访问,就不应将该访问用于评估与另一个访问的重复。让我用一个例子来解释。

end_date

在上面的示例中,患者进行了5次就诊。访问2的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 在访问1的begin_date + 90天范围内,因此应标记访问2。标记访问2后,该行就不应在分析中用于以后的任何访问。从概念上讲,这就像删除访问2并重新开始分析。

过渡阶段(访问2被删除,分析再次开始)

end_date

因此,即使访问3与访问2重叠,由于访问2已被删除,因此访问3不会被标记为上一次访问(现在是访问1)超过访问3的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 + 90天end_date。然后,应标记访问4,因为它与未标记的访问重叠(访问3)。因此,由于标记了访问4,因此访问5将因为其begin_date位于访问3的begin_date + 90天之内而被删除。

预期输出

end_date

@gordonlinoff回答了一个非常相似的问题here,但是我遇到了使用递归CTE的问题。问题之间的区别在于,该问题需要引用另一列(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 ),而不是单个日期列。递归CTE对我来说仍然是一个新概念,但是我希望这将有助于巩固这一概念。

我为解决这个难题所做的尝试(@gordonlinoff的小猪支持):

end_date

我的编辑基于结果不正确地引用了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, 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

1 个答案:

答案 0 :(得分:1)

我调整了您的示例数据,以使访问5在访问3的结束日期+ 90天的范围内。访问3的结束日期是2018-04-22。如果我们加上90天,则为2018-07-21。您问题中的样本数据以2018-07-23作为第5个访问日期,与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 |
+-------+-----------+------------+------------+------+