如何捕获分组中的第一行和每行至少相隔 15 天的后续行?
How to capture first row in a grouping and subsequent rows that are each a minimum of 15 days apart?
假设给定的保险仅支付 15 天内看同一位医生一次的同一位患者的费用。如果患者在这 15 天内来看医生一次、两次或二十次,医生将只得到一次付款。如果患者在第 16 天或第 18 天或第 29 天(或所有这三个!)再次来访,医生将获得第二笔付款。第一次访问(或 15 天间隔后的第一次访问)始终是必须与其投诉一起计费的。
所有访问的 SQL 可以松散地表示如下:
SELECT VisitID
,PatientID
,VisitDtm
,DoctorID
,ComplaintCode
FROM Visits
目标是以一种仅捕获可计费事件的方式查询访问 table。
我一直在努力解决这个本质上与非常相似的问题。然而,这对我不起作用的原因是,正如公认的回答者 (Salman A) 指出的那样,请注意,这可以将更长的日期范围组合在一起,例如01-01、01-11、01-21、02-01 和 02-11 将被组合在一起尽管第一个和最后一个日期相隔超过 15 天。 这对我来说是一个问题,因为要求在第一次事件发生 15 天后始终捕获下一次事件。
我花了好几个小时来思考这个问题并仔细研究类似的问题,并且正在寻求帮助来理解解决方案的路径,不一定是实际的代码解决方案。如果在代码解决方案的上下文中更容易回答,那很好。非常感谢任何和所有指导!
此类任务需要一个迭代过程,因此您可以跟踪上次计费访问。一种方法是递归 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)
。
这是另一种方法。与 GMB 类似,这会向 CTE 中的访问 table 添加 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;
假设给定的保险仅支付 15 天内看同一位医生一次的同一位患者的费用。如果患者在这 15 天内来看医生一次、两次或二十次,医生将只得到一次付款。如果患者在第 16 天或第 18 天或第 29 天(或所有这三个!)再次来访,医生将获得第二笔付款。第一次访问(或 15 天间隔后的第一次访问)始终是必须与其投诉一起计费的。
所有访问的 SQL 可以松散地表示如下:
SELECT VisitID
,PatientID
,VisitDtm
,DoctorID
,ComplaintCode
FROM Visits
目标是以一种仅捕获可计费事件的方式查询访问 table。
我一直在努力解决这个本质上与
我花了好几个小时来思考这个问题并仔细研究类似的问题,并且正在寻求帮助来理解解决方案的路径,不一定是实际的代码解决方案。如果在代码解决方案的上下文中更容易回答,那很好。非常感谢任何和所有指导!
此类任务需要一个迭代过程,因此您可以跟踪上次计费访问。一种方法是递归 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)
。
这是另一种方法。与 GMB 类似,这会向 CTE 中的访问 table 添加 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;