我正在寻找几个日期范围内的天数。我使用datediff函数来计算天数,但现在我想排除重叠天数。因此,从最古老的日期开始,直到我想要在日期范围内有天数,如果它在重叠范围内,则每天只计算一次。
我的表格如下:
Person_id | Start_date | End_date | Count
83244 2014-09-01 00:00:00 2014-09-06 00:00:00 5
83244 2014-09-08 00:00:00 2015-09-07 00:00:00 364
83244 2015-01-15 00:00:00 2015-02-01 00:00:00 17
如果我总结一下,我会得到382,但我要找的答案是369.因为最后一行与第二行完全重叠。
有没有人有解决方案?
答案 0 :(得分:1)
我用第二个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具有某种程序感觉&#34;当谈到select
陈述时。实际上,您可以在其中使用变量(@id
和@ed
),这些变量会随着时间的推移而改变其状态(在此上下文中,order by
子句非常重要。)
此查询背后的基本思想是:从某个pid
开始,按照增加开始日期(sd
)的顺序列出间隔。始终记住变量ed
中结束日期(@ed
)的最大值。现在,对于每个新间隔,检查是否与前一个间隔重叠,即。即检查当前开始日期sd
是否小于上一个(最长)结束日期(@ed
)并相应地计算间隔days
。
当前case
更改时,必须使用第一个@id
子句重置变量@ed
和pid
。
子查询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。
如果您只对总金额感兴趣,您当然可以将整个查询包装在另一个group
中,如下所示:
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
答案 1 :(得分:0)
此SQL将返回不计算重叠的天数之和:
select person_id, sum(days)
from (
select t1.person_id,
t1.start_date,
t1.end_date,
case when t1.end_date > coalesce(greatest(max(t2.end_date), t1.start_date), t1.start_date)
then datediff(t1.end_date, coalesce(greatest(max(t2.end_date), t1.start_date), t1.start_date))
else 0
end days
from t t1
left join t t2 on t1.person_id = t2.person_id
and (t2.start_date < t1.start_date
or t2.start_date = t1.start_date and t2.end_date < t1.end_date)
group by t1.person_id,
t1.start_date,
t1.end_date
) detail
group by person_id
要求给定人员的句点是唯一的,因此没有两个句点与 end_date 具有相同的 start_date 。
此fiddle为样本数据和人员返回369。
你可以创建一个序列表(这对许多用途很有用),然后用它计算唯一的天数。
因此,作为一次性操作,您将使用包含自然数字(0,1,2 ......)的附加表扩展数据库模型:
create table sequence (
num int,
primary key (num)
);
// Populate the above table with as many numbers as needed:
insert into sequence values(0);
insert into sequence select num+ 1 from sequence; -- 2 records
insert into sequence select num+ 2 from sequence; -- 4 records
insert into sequence select num+ 4 from sequence; -- 8 records
insert into sequence select num+ 8 from sequence; -- 16 records
insert into sequence select num+ 16 from sequence; -- 32 records
insert into sequence select num+ 32 from sequence; -- 64 records
insert into sequence select num+ 64 from sequence; -- 128 records
insert into sequence select num+ 128 from sequence; -- 256 records
insert into sequence select num+ 256 from sequence; -- 512 records
insert into sequence select num+ 512 from sequence; -- 1024 records
insert into sequence select num+1024 from sequence; -- 2048 records
insert into sequence select num+2048 from sequence; -- 4096 records
您可以继续插入这样的记录,但对于当前任务来说,这已经足够了。
现在到实际的解决方案:
select person_id, count(distinct num), count(num)
from sequence
cross join (select min(start_date) min_date,
max(end_date) max_date
from t) stats
inner join t
on date_add(min_date, interval (num*24+12) hour)
between start_date and end_date
where num < datediff(max_date, min_date)
group by person_id
此查询使用唯一数字从最早的开始日期开始获取天数,并包括它们在某个时段中的日期。然后它计算满足该条件的唯一日期。
where
子句是可选的,但会加快查询速度。
这是fiddle。它产生了这个结果:
| Person_id | count(distinct num) | count(num) |
|-----------|---------------------|------------|
| 83244 | 369 | 386 |