我已经用第二个Person_id 填充了您的示例,并稍微缩短了列名以使代码更短:
CREATE TABLE tbl(`pid` int, `sd` datetime, `ed` datetime);
INSERT INTO tbl (`pid`, `sd`, `ed`)
VALUES
(83244, '2014-09-01', '2014-09-06'),
(83244, '2014-09-08', '2015-09-07'),
(83243, '2014-08-08', '2015-08-15'),
(83243, '2014-08-11', '2015-09-03'),
(83244, '2015-01-15', '2015-02-01');
因此,在处理上述数据时,您可以应用以下查询:
SELECT pid,sd,ed,CASE WHEN @id!=pid THEN @id:=pid+0*(@ed:=Date('1970-1-1')) END id,
CASE WHEN sd<@ed THEN CASE WHEN ed>@ed THEN datediff(ed,@ed) ELSE 0 END
ELSE datediff(ed,sd) END days,
@ed:=CASE WHEN ed>@ed THEN ed ELSE @ed END enddt
FROM tbl,( select @id:=0 ) const
ORDER BY pid,sd
与其他 RDBMS 不同,MySql 对 select 语句有一定的“程序感”。您实际上可以在其中使用变量(@id 和 @ed),它们会随着时间的推移改变它们的状态(在这种情况下,末尾的 order by 子句非常重要)。
此查询背后的基本思想是:从某个pid 开始,并按开始日期递增的顺序列出间隔(sd)。始终记住变量 @ed 中的结束日期的最大值 (ed)。现在,对于每个新间隔,检查是否与前一个间隔重叠,即。 e.检查当前开始日期sd是否小于上一个(最大)结束日期(@ed)并相应地计算间隔days。
每当当前pid 更改时,第一个case 子句是重置变量@id 和@ed 所必需的。
子查询const只是在开头设置了变量@id。
查询产生以下结果:
pid sd ed id days enddt
83243 2014-08-08 00:00:00 2015-08-15 00:00:00 83243 372 2015-08-15 00:00:00
83243 2014-08-11 00:00:00 2015-09-03 00:00:00 19 2015-09-03 00:00:00
83244 2014-09-01 00:00:00 2014-09-06 00:00:00 83244 5 2014-09-06 00:00:00
83244 2014-09-08 00:00:00 2015-09-07 00:00:00 364 2015-09-07 00:00:00
83244 2015-01-15 00:00:00 2015-02-01 00:00:00 0 2015-09-07 00:00:00
在此处查看Demo。
如果您只对总和感兴趣,您当然可以将整个查询包装在另一个 grouping 中,如下所示:
SELECT pid,sum(days) FROM (
SELECT pid,sd,ed,CASE WHEN @id!=pid THEN @id:=pid+0*(@ed:=Date('1970-1-1')) END id,
CASE WHEN sd<@ed THEN CASE WHEN ed>@ed THEN datediff(ed,@ed) ELSE 0 END
ELSE datediff(ed,sd) END days,
@ed:=CASE WHEN ed>@ed THEN ed ELSE @ed END enddt
FROM tbl,( select @id:=0 ) const
ORDER BY pid,sd
) t GROUP BY pid ORDER BY pid
这会得到你
pid sum(days)
83243 391
83244 369