如何捕获分组中的第一行以及每行至少相隔 15 天的后续行?

Posted

技术标签:

【中文标题】如何捕获分组中的第一行以及每行至少相隔 15 天的后续行?【英文标题】:How to capture first row in a grouping and subsequent rows that are each a minimum of 15 days apart? 【发布时间】:2020-09-02 20:55:26 【问题描述】:

假设给定的保险只会为同一位患者在 15 天内拜访同一位医生一次的费用支付费用。如果患者在这 15 天内去看医生一次、两次或二十次,医生将只获得一次付款。如果患者在第 16 天或第 18 天或第 29 天(或全部三个!)再次出现,医生将获得第二次付款。第一次访问(或 15 天间隔后的第一次访问)始终是必须计费的一次,连同其投诉。

所有访问的SQL可以大致表达如下:

SELECT  VisitID
       ,PatientID
       ,VisitDtm
       ,DoctorID
       ,ComplaintCode
FROM Visits

目标是以仅捕获可计费事件的方式查询访问表。

我一直在尝试解决这个问题,它本质上与Group rows with that are less than 15 days apart and assign min/max date 非常相似。但是,这对我不起作用的原因是,正如公认的回答者 (Salman A) 指出的那样,请注意,这可能会将更长的日期范围组合在一起,例如01-01、01-11、01-21、02-01 和 02-11 将被组合在一起虽然第一个日期和最后一个日期相隔超过 15 天对我来说是个问题,因为要求始终在第一次事件过去 15 天后捕获下一个事件。

我已经花了好几个小时来思考这个问题并仔细研究类似的问题,并且正在寻求帮助以了解解决方案的路径,而不一定是实际的代码解决方案。如果在代码解决方案的上下文中更容易回答,那很好。非常感谢任何和所有指导!

【问题讨论】:

【参考方案1】:

这种类型的任务需要一个迭代过程,以便您可以跟踪最后一次计费访问。一种方法是递归 cte。

您通常会使用row_number() 枚举每位患者的就诊次数,然后从第一次就诊开始遍历数据集,同时跟踪最后一次“可计费”就诊。一旦遇到比上一次计费访问晚 15 天以上的访问,该值就会重置。

with 
    data as (
        select visitid, patientid, visitdtm, doctorid,
            row_number() over(partition by patientid order by visitdtm) rn
        from visits
    ),
    cte as (
        select d.*, visitdtm as billabledtm from data d where rn = 1
        union all
        select d.*, 
            case when d.visitdtm >= dateadd(day, 15, c.billabledtm)
                then d.visitdtm
                else c.billabledtm
            end
        from cte c
        inner join data d 
            on d.patientid = c.patientid and d.rn = c.rn + 1
    )
select * from cte where visitdtm = billabledtm order by patientid, rn

如果患者的就诊次数可能超过 100 次,那么您需要在查询的最后添加 option (maxrecursion 0)

【讨论】:

由于真实数据的复杂性,我不得不稍微尝试一下,但它现在确实有效(我不得不说,递归 CTE 非常有趣!) 如果我可能会问,引导您进行递归 CTE 的思考过程是什么?是不是后面的记录是相对于前面的但不是前面的记录(LAG/LEAD 可以工作)? @RiSt:是的,这就是问题所在。我们需要选择一条记录,保留它直到满足下一个有效记录,然后reiterate。这对于窗口函数是不可能的。【参考方案2】:

这是另一种方法。与 GMB 类似,这会在 CTE 中的 Visits 表中添加一个 row_number,但它也会添加 VisitDtm 之间的提前期日期差异。然后它对日期差进行累积“总和”并除以 15。当该商增加一个整数时,它表示数据中的一个计费事件。

类似的东西

;with lead_cte as (
    select v.*, row_number() over (partition by PatientId order by VisitDtm) rn,
           datediff(d, VisitDtm, lead(VisitDtm) over (partition by PatientId order by VisitDtm)) lead_dt_diff
    from Visits v),
cum_sum_cte as (
    select lc.*, sum(lead_dt_diff) over (partition by PatientId order by VisitDtm)/15 cum_dt_diff 
    from lead_cte),
min_billable_cte as (
    select PatientId, cum_dt_diff, min(rn) min_rn
    from cum_sum_cte
    group by PatientId, cum_dt_diff)
select lc.* 
from lead_cte lc
     join min_billable_cte mbc on lc.PatintId=mbc.PatientId
                                  and lc.rn=mbc.min_rn;

【讨论】:

我也想试试这个方法,但因为 GMB 是第一个我尝试了这种方法并且它奏效了。

以上是关于如何捕获分组中的第一行以及每行至少相隔 15 天的后续行?的主要内容,如果未能解决你的问题,请参考以下文章

SQL如何选择所有重复id的行,但不是每行的第一行?

选择最后一组连续行中的第一行

具有不同量词的正则表达式捕获组

PostgreSQL 中的分组限制:显示每个组的前 N ​​行,但仅当这些行中的第一行等于特定数据时

如何在 oracle 查询中获取字符串的第一行?

分组日期之间的间隔