使用MYSQL计算每天的约会

时间:2012-11-14 09:51:25

标签: mysql select count group-by

我在一个给定时间内计算一天约会的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。

  • StartingDate = 2012-11-14 17:00:00,EndingDate = 2012-11-15 08:00:00
  • StartingDate = 2012-11-15 09:00:00,EndingDate = 2012-11-15 10:00:00
  • StartingDate = 2012-11-15 11:00:00,EndingDate = 2012-11-15 12:00:00

我的陈述在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)

1 个答案:

答案 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

现在你要做的是

  1. 选择一个天数,您可以选择-- 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 表格中的数字并将其转换为日期。
  2. 然后加入约会到这些日期,以便特定日期的约会加入该特定日期
  3. 然后每天对所有这些约会进行分组并获得结果
  4. 这是执行此操作的代码:

    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;