我在一个给定时间内计算一天约会的mysql语句遇到了麻烦。我有一个日历表,包括起始和完成列(type = DateTime)。以下陈述应计算11月的所有约会,包括总预约:
SELECT
COUNT('APPOINTMENTS') AS Count,
DATE(c.StartingDate) AS Datum
FROM t_calendar c
WHERE
c.GUID = 'blalblabla' AND
((DATE(c.StartingDate) <= DATE('2012-11-01 00:00:00')) AND (DATE(c.EndingDate) >= DATE('2012-11-30 23:59:59'))) OR
((DATE(c.StartingDate) >= DATE('2012-11-01 00:00:00')) AND (DATE(c.EndingDate) <= DATE('2012-11-30 23:59:59')))
GROUP BY DATE(c.StartingDate)
HAVING Count > 1
但是如何包含在StartingDate之前开始并以StartingDate结束的约会?
e.g。
我的陈述在11月15日返回2。但这是错误的,因为缺少第一次约会。如何包括这些约会?我缺少什么,UNION SELECT,JOIN,sub selection?
SELECT
c1.GUID, COUNT('APPOINTMENTS') + COUNT(DISTINCT c2.ANYFIELD) AS Count,
DATE(c1.StartingDate) AS Datum,
COUNT(DISTINCT c2.ANYFIELD)
FROM
t_calendar c1
LEFT JOIN
t_calendar c2
ON
c2.ResourceGUID = c1.ResourceGUID AND
(DATE(c2.EndingDate) = DATE(c1.StartingDate)) AND
(DATE(c2.StartingDate) < DATE(c1.StartingDate))
WHERE
((DATE(c1.StartingDate) <= DATE('2012-11-01 00:00:00')) AND (DATE(c1.EndingDate) >= DATE('2012-11-30 23:59:59'))) OR
((DATE(c1.StartingDate) >= DATE('2012-11-01 00:00:00')) AND (DATE(c1.EndingDate) <= DATE('2012-11-30 23:59:59')))
GROUP BY
c1.ResourceGUID,
DATE(c1.StartingDate)
答案 0 :(得分:4)
首先,您的两个范围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个只有一个,因此被排除因为{ {1}}并且不是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
可能还有其他方法,但最常见的方法是使用数字或?calendar *表格,使您能够将范围划分为单个点 - 天。他们必须将您的约会加入此数字表并提供结果。
我created a SQLFiddle可以解决问题。这是它的作用......
假设您的数字表having Count > 0
的数字从0到 x 。和约会表Num
包含您的记录。下面的脚本创建了这两个表并填充了一些数据。数字最多只能达到100,足以满足3个月的数据。
Cal
现在你要做的是
-- 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;