【问题标题】:Counting appointments for each day using MYSQL使用 MYSQL 计算每天的约会
【发布时间】:2012-11-02 19:23:41
【问题描述】:

我在使用 mysql 语句计算给定时间段内一天的约会时遇到了麻烦。我有一个日历表,包括开始和结束列(类型 = 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 结束的约会?

例如

  • 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、子选择?


可能的解决方案?

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)

【问题讨论】:

  • 您的可能的解决方案应该怎么做?看来您使事情变得过于复杂并加入了我们甚至不知道的数据...您是否尝试过使用数字表的最终解决方案,它更简单并准确返回您需要的内容?
  • 您的join 搜索条件似乎没有意义,除非您的约会的开始日期之后 结束日期...如果您这样做,编写查询会更容易它逐步进行,因此您添加的每个更改都足够简单,易于处理和理解。 或者你真的没有明确自己想要完成什么。点击我的SQLFiddle,你会看到正确且执行良好的正确结果。

标签: mysql select count group-by


【解决方案1】:

第一:合并范围检查

首先,您的两个范围 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并且不是&gt; 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

获取任何给定日期范围内每天的约会次数

可能还有其他方法,但最常见的方法是使用 numbers 或 ?calendar* 表,让您能够将范围分解为单个 - 天。他们必须将您的约会加入此数字表并提供结果。

我有 created a 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

现在你要做的是

  1. 选择日期范围,您可以通过从Num 表中选择数字并将它们转换为日期来完成。
  2. 然后将您的约会加入到这些日期,以便将那些在特定日期的约会加入到该特定日期
  3. 然后只需每天将所有这些约会分组并获得结果

这是执行此操作的代码:

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

【讨论】:

  • 嘿!非常感谢。我刚刚更改了范围检查!但是我的问题没有解决。我想将在 11 月 15 日结束的约会数量添加到 11 月 15 日,前提是他们的开始日期在 11 月 15 日之前......例如- 2012.11.14 计数 = 1 - 2012.11.15 计数 = 3
  • @user1696336:好的。您真正想要的是获得在特定日期的所有约会的计数,而不管它们的开始和结束时间是什么?对吗?
  • 我知道...我只是尝试了子选择并加入,但我还没有解决问题。我希望你能找到任何解决方案:)
  • @user1696336:好吧,这不是一件容易的事,因为您似乎必须推断记录......
  • 对不起,我已经有一段时间了 :( 很酷 :) 只是见过的最好的解决方案!!!谢谢 :)!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-29
  • 1970-01-01
  • 2015-04-11
相关资源
最近更新 更多