首先,如果您要工作几天,您应该考虑使用Calendar Table。它简化了事情。
create table Calendar
(
id int primary key identity,
date datetime not null
--various computed date attributes that we elide for now...
)
--populate with few years worth of days:
declare @dt datetime
set @dt = '1/1/2017'
while @dt <= '12/31/2020'
begin
insert Calendar select @dt
set @dt = dateadd(day, 1, @dt)
end
因此,您的架构可能看起来像这样:
create table Drug
(
id int primary key identity,
name nvarchar(100) not null
)
create table Patient
(
id int primary key identity,
name nvarchar(100) not null
)
create table DrugTrial
(
patient int foreign key references Patient,
drug int foreign key references Drug,
date int foreign key references Calendar,
supply int
)
关于此架构,您的示例数据是:
insert Patient select 'ABC'
insert Drug select 'A'
union select 'B'
insert DrugTrial
select 1, 1, 1, 30 union
select 1, 1, 31, 30 union
select 1, 2, 61, 30 union
select 1, 1, 91, 30
我们可以通过单个常规查询获得所需的结果集,但为了清楚起见,我们将使用一系列公用表表达式。
首先,我们生成具有前驱的所有试验的集合。这意味着我们希望在使用相同药物和患者的试验之后立即进行所有试验:
with Q as
(
select T.* from DrugTrial S
cross apply
(
select * from DrugTrial T
where T.date = S.date + S.supply and
T.patient = S.patient and T.drug = S.drug
) T
),
接下来,我们需要计算位于序列开头的试验集。但这很容易,因为它只是所有试验的集合减去具有前驱的试验子集(如上面在 Q 中定义的)。
P as
(
select patient, drug, date, supply from DrugTrial
except select patient, drug, date, supply from Q
),
最后,我们使用递归查询来构建序列:
R as
(
select *, row_number() over (order by date) as seq from P
union all
select Q.*, S.seq from Q cross apply
(select * from R
where Q.date = R.date + R.supply
and Q.patient = R.patient and R.drug = Q.drug) S
)
R 的基本情况只是集合P,我们使用row_number 函数对其进行扩充以生成我们的序列号。 R 的递归情况只是为 R 中的每个试验计算后继试验(如果有)。
把它们放在一起:
select
Pt.id patient_id, Pt.name patient_name,
D.id drug_id, D.name drug_name,
R.supply, R.date, R.seq
from R inner join Patient Pt on Pt.id = R.patient
inner join Drug D on D.id = R.drug
inner join Calendar C on C.id = R.date order by R.date
产生结果:
patient_id patient_name drug_id drug_name supply date seq
----------- ---------------- ----------- ------------- ----------- ------ ---
1 ABC 1 A 30 1 1
1 ABC 1 A 30 31 1
1 ABC 2 B 30 61 2
1 ABC 1 A 30 91 3
您可以看到整个解决方案here。它被封装在一个未提交的事务中,以便于使用。