第一:合并范围检查
首先,您的两个范围where
条件可以用一个替换。而且似乎您只计算完全重叠目标日期范围或完全包含在其中的约会。不包括部分重叠的。因此,您关于在范围开始日期结束的约会的问题。
为了使where
子句易于理解,我将使用以下方法对其进行简化:
- 定义目标范围的两个变量:
rangeStart
(在你的情况下,2012 年 11 月 1 日)
rangeEnd
(我宁愿假设到 2012 年 12 月 1 日 00:00:00.00000)
- 不会像您那样
datetime
仅转换为日期(使用date
函数),但您可以轻松地做到这一点。
考虑到这些,您的where
条款可以大大简化并涵盖给定范围的所有约会:
...
where (c.StartingDate < rangeEnd) and (c.EndingDate >= rangeStart)
...
这将搜索所有在目标范围内的约会,并将涵盖所有这些约会案例:
start end
target range |==============|
partial front |---------|
partial back |---------|
total overlap |---------------------|
total containment |-----|
部分正面/背面也可能几乎不会触及您的目标范围(您一直在追求的范围)。
二:解决问题
为什么你错过了第一张唱片?仅仅因为您的having
条款仅收集从某一天开始有超过 1 个约会的团体:11 月 15 日有两个,但 14 日只有一个,因此被排除在外,因为Count = 1
不是> 1
。
要回答您的第二个问题,我缺少的是:您没有遗漏任何东西,实际上您的陈述中有太多内容需要简化。
试试这个语句,而不是应该返回你所追求的:
select count(c.GUID) as Count,
date(c.StartingDate) as Datum
from t_calendar c
where (c.GUID = 'blabla') and
(c.StartingDate < str_to_date('2012-12-01', '%Y-%m-%d') and
(c.EndingDate >= str_to_date('2012-11-01', '%Y-%m-%d'))
group by date(c.StartingDate)
我使用str_to_date
函数使字符串到日期的转换更加安全。
我不太确定您为什么要having
在声明中包含它,因为它并不是真正需要的。除非您的实际陈述更复杂,并且您只包含最相关的部分。在这种情况下,您可能必须将其更改为:
having Count > 0
在任何给定日期范围内获取每天的约会计数
可能还有其他方法,但最常见的方法是使用数字或 ?calendar* 表,使您能够将范围分解为单个点- 天。他们必须将您的约会加入此数字表并提供结果。
我创建了一个可以解决问题的 SQLFiddle。这就是它的作用......
假设您有数字表Num
,其中数字从 0 到x。和约会表Cal
与您的记录。以下脚本创建了这两个表并填充了一些数据。数字最多只有 100 个,足以存储 3 个月的数据。
-- appointments
create table Cal (
Id int not null auto_increment primary key,
StartDate datetime not null,
EndDate datetime not null
);
-- create appointments
insert Cal (StartDate, EndDate)
values
('2012-10-15 08:00:00', '2012-10-20 16:00:00'),
('2012-10-25 08:00:00', '2012-11-01 03:00:00'),
('2012-11-01 12:00:00', '2012-11-01 15:00:00'),
('2012-11-15 10:00:00', '2012-11-16 10:00:00'),
('2012-11-20 08:00:00', '2012-11-30 08:00:00'),
('2012-11-30 22:00:00', '2012-12-05 00:00:00'),
('2012-12-01 05:00:00', '2012-12-10 12:00:00');
-- numbers table
create table Nums (
Id int not null primary key
);
-- add 100 numbers
insert into Nums
select a.a + (10 * b.a)
from (select 0 as a union all
select 1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6 union all
select 7 union all
select 8 union all
select 9) as a,
(select 0 as a union all
select 1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6 union all
select 7 union all
select 8 union all
select 9) as b
现在你要做的是
Num
通过从表格中选择数字并将它们转换为日期来选择您所做的日期范围。
- 然后将您的约会加入这些日期,以便将那些在特定日期的约会加入到该特定日期
- 然后每天将所有这些约会分组并获得结果
这是执行此操作的代码:
-- just in case so comparisons don't trip over
set names 'latin1' collate latin1_general_ci;
-- start and end target date range
set @s := str_to_date('2012-11-01', '%Y-%m-%d');
set @e := str_to_date('2012-12-01', '%Y-%m-%d');
-- get appointment count per day within target range of days
select adddate(@s, n.Id) as Day, count(c.Id) as Appointments
from Nums n
left join Cal c
on ((date(c.StartDate) <= adddate(@s, n.Id)) and (date(c.EndDate) >= adddate(@s, n.Id)))
where adddate(@s, n.Id) < @e
group by Day;
这是这个相当简单的选择语句的结果:
| DAY | APPOINTMENTS |
-----------------------------
| 2012-11-01 | 2 |
| 2012-11-02 | 0 |
| 2012-11-03 | 0 |
| 2012-11-04 | 0 |
| 2012-11-05 | 0 |
| 2012-11-06 | 0 |
| 2012-11-07 | 0 |
| 2012-11-08 | 0 |
| 2012-11-09 | 0 |
| 2012-11-10 | 0 |
| 2012-11-11 | 0 |
| 2012-11-12 | 0 |
| 2012-11-13 | 0 |
| 2012-11-14 | 0 |
| 2012-11-15 | 1 |
| 2012-11-16 | 1 |
| 2012-11-17 | 0 |
| 2012-11-18 | 0 |
| 2012-11-19 | 0 |
| 2012-11-20 | 1 |
| 2012-11-21 | 1 |
| 2012-11-22 | 1 |
| 2012-11-23 | 1 |
| 2012-11-24 | 1 |
| 2012-11-25 | 1 |
| 2012-11-26 | 1 |
| 2012-11-27 | 1 |
| 2012-11-28 | 1 |
| 2012-11-29 | 1 |
| 2012-11-30 | 2 |