2012-02-14 79 views
6

据我所知,关于周期性事件的问题很常见,但我一直没能找到一个与回答中反复比其他事件做日历应用,这个具体的问题。主要区别在于我们的应用中的事件。将只会在报告中或单独看到,而不是以日历格式显示,尽管在很多方面它们非常相似,可能只是少了与日历相关的行李。周期性事件,SQL查询

以类似于日历应用的方式。事件既可以一次性发生,也可以重复发生。每个星期四或每个月的第一个星期一,直到将来某个预先设定的时间。

事件被存储在包含开始和结束日期和“复发的类型ID”事件表。如果“重复类型”为“无”,那么开始日期和结束日期将相同。事件表保存一个id到一个单独的表,该表包含事件类型名称,例如。 '会议'或'每周报告'

还有一张表,其中包含'重复类型'的列表,例如: '不再发生','每个星期一','月的第一个星期一'以及'月的最后一个星期六'。

为了更容易查找另一个表包含日期1960至2060年的列表,以及关于每个日期的相关信息,如是否是周一以来,和周一的发生是在一个月。

这使得查找,如:

 
SELECT DISTINCT(e.eventid),n.nameid,n.firstname,n.lastname,d.dt,r.recurring 
FROM dates d 
LEFT JOIN recurringtypes r 
/* if event recurring every week E.g. 'Every Monday' */ 
ON (r.rectypeid BETWEEN 2 AND 8 AND r.day = d.dow) 
/* if event recurring every month E.g. 'First Monday, every month' */ 
OR ((r.rectypeid BETWEEN 9 AND 36) AND r.day = d.dow AND r.occurrence = d.occurrence) 
/* if event recurring every last week of month E.g. 'Last Monday, every month' */ 
OR (r.rectypeid >= 37 AND r.day = d.dow and r.islast = d.islast) 
LEFT JOIN events e on e.rectypeid = r.rectypeid 
LEFT JOIN eventtypes t ON e.eventtypeid = t.eventtypeid 
LEFT JOIN names n ON e.namesid = n.namesid 
WHERE (d.dt BETWEEN '2012/02/01' AND '2012/05/01') 
ORDER BY d.dt; 

这正是需要寻找重复事件,使输出,如:

 
+-----------+---------------+-------------------+-----------+------------+-------------------------------+ 
| eventid | nameid  | lastname   | firstname | dt   | recurring      | 
+-----------+---------------+-------------------+-----------+------------+-------------------------------+ 
| 3291788 |  1728449 | smith    | zoe  | 2012-02-02 | First Thursday, every month | 
| 3291797 |  1765432 |     |   | 2012-02-05 | First Sunday, every month  | 
| 3291798 |  1730147 |     |   | 2012-02-05 | First Sunday, every month  | 
| 3291803 |  1790061 | Carpenter   | Richie | 2012-02-06 | Every Monday     | 
| 3291805 |  1790061 | Carpenter   | Richie | 2012-02-08 | Second Wednesday, every month | 
| 3291803 |  1790061 | Carpenter   | Richie | 2012-02-13 | Every Monday     | 
| 3291799 |  1790061 | Carpenter   | Richie | 2012-02-15 | Third Wednesday, every month | 
| 3291803 |  1790061 | Carpenter   | Richie | 2012-02-20 | Every Monday     | 

为了获得无经常性项目一个简单的查询可用于:

 
SELECT n.nameid,n.lastname,n.firstname,e.firstdate,e.eventid,'No' as Recurring 
FROM events e 
LEFT JOIN names n ON n.names = e.namesid 
AND e.rectypeid <= 1 
AND e.firstdate BETWEEN '2012/02/01' AND '2012/05/01' 
AND e.eventid IS NOT NULL ORDER BY e.firstdate; 
这给出的输出与第一个查询非常相似,但关键的是,日期来自事件表而非日期表。

我的问题是:如何组合这些查询来创建一个包含所有事件的列表,包括按日期顺序的循环和非循环?


这些都是表,并从中选择缩短,一些列的,为简洁各项指标已被删除:)。由于相同的原因,“名称”表并未包含在内。

 
CREATE TABLE events (
eventid int(11) NOT NULL AUTO_INCREMENT, 
eventtypeid int(11) DEFAULT '0', 
firstdate date DEFAULT '1960-01-01' COMMENT 'First event', 
lastdate date DEFAULT '1960-01-01' COMMENT 'Last event', 
rectypeid int(11) DEFAULT '1' 
); 
+---------+-------------+------------+------------+-----------+ 
| eventid | eventtypeid | firstdate | lastdate | rectypeid | 
+---------+-------------+------------+------------+-----------+ 
| 3291803 |   16 | 2012-02-03 | 2012-04-11 |   3 | 
| 3291797 |   8 | 2012-02-12 | 2012-02-22 |   9 | 
| 3291798 |   5 | 2012-02-12 | 2012-02-12 |   9 | 
| 3291788 |   8 | 2012-05-24 | 2015-01-16 |  13 | 
| 3291805 |   10 | 2012-01-04 | 2012-02-14 |  19 | 
| 3291799 |   16 | 2012-02-07 | 2012-10-24 |  26 | 
| 3291804 |   5 | 2012-02-03 | 2012-08-22 |  41 | 
+---------+-------------+------------+------------+-----------+ 
CREATE TABLE cmseventtypes (
eventtypeid int(11) NOT NULL AUTO_INCREMENT, 
eventtype varchar(50) DEFAULT '' COMMENT 'Event type AKA name' 
); 
+-------------+----------------------+ 
| eventtypeid | eventype    | 
+-------------+----------------------+ 
|   1 | Follow up meeting | 
|   2 | Reminder email due | 
|   3 | Monthly meeting  | 
|   4 | Weekly report  | 
|   5 | Golf practice  | 
+------------------------------------+ 
CREATE TABLE recurringtypes (
rectypeid int(11) NOT NULL AUTO_INCREMENT, 
recurring varchar(40) DEFAULT '', 
day tinyint(4) DEFAULT '0', 
occurrence tinyint(4) DEFAULT '0', 
islast tinyint(4) DEFAULT '0' 
); 
+-----------+---------------------------+------+------------+--------+ 
| rectypeid | recurring     | day | occurrence | islast | 
+-----------+---------------------------+------+------------+--------+ 
|   1 | No      | 0 |   0 |  0 | 
|   2 | Every Sunday    | 1 |   0 |  0 | 
|   3 | Every Monday    | 2 |   0 |  0 | 
|   4 | Every Tuesday    | 3 |   0 |  0 | 
|   5 | Every Wednesday   | 4 |   0 |  0 | 
|   6 | Every Thursday   | 5 |   0 |  0 | 
|   7 | Every Friday    | 6 |   0 |  0 | 
|   8 | Every Saturday   | 7 |   0 |  0 | 
|   9 | First Sunday, every month | 1 |   1 |  0 | 
|  10 | First Monday, every month | 2 |   1 |  0 | 
+-----------+---------------------------+------+------------+--------+ 
CREATE TABLE dates (
dt date NOT NULL COMMENT 'Date', 
daycount mediumint(9) NOT NULL DEFAULT '1', 
year smallint(6) NOT NULL DEFAULT '1970', 
month tinyint(4) NOT NULL DEFAULT '1', 
dom tinyint(4) NOT NULL DEFAULT '1', 
dow tinyint(4) NOT NULL DEFAULT '1', 
occurrence tinyint(4) NOT NULL DEFAULT '0', 
islast tinyint(1) NOT NULL DEFAULT '0' 
); 
+------------+----------+------+-------+-----+-----+------------+--------+ 
| dt   | daycount | year | month | dom | dow | occurrence | islast | 
+------------+----------+------+-------+-----+-----+------------+--------+ 
| 2012-02-02 | 734900 | 2012 |  2 | 2 | 5 |   1 |  0 | 
| 2012-02-03 | 734901 | 2012 |  2 | 3 | 6 |   1 |  0 | 
| 2012-02-04 | 734902 | 2012 |  2 | 4 | 7 |   1 |  0 | 
| 2012-02-05 | 734903 | 2012 |  2 | 5 | 1 |   1 |  0 | 
| 2012-02-06 | 734904 | 2012 |  2 | 6 | 2 |   1 |  0 | 
| 2012-02-07 | 734905 | 2012 |  2 | 7 | 3 |   1 |  0 | 
| 2012-02-08 | 734906 | 2012 |  2 | 8 | 4 |   2 |  0 | 
| 2012-02-09 | 734907 | 2012 |  2 | 9 | 5 |   2 |  0 | 
+------------+----------+------+-------+-----+-----+------------+--------+ 


我们绝对不使用上面的代码或表格的布局设置,任何工作解决方案将受到欢迎。请不要点我朝:

How would you store possibly recurring times?

What's the best way to model recurring events in a calendar application?

Should I store dates or recurrence rules in my database when building a calendar app?

http://tools.ietf.org/html/rfc5545

我已经检查出来,他们是非常有用的,但不是在做和我们打算的一样。

TIA

+0

'islast'做什么?或'日期'表中的'发生'? – Naltharial 2012-02-14 14:27:04

+0

如果设置'islast'标志一个月中某一天的最后一次发生(例如'月份的上个星期一'),则发生是一个月中某一天的发生次数(例如'月的第一个星期一','第二个星期一的月') – blankabout 2012-02-14 14:31:15

回答

2

除非我失去了一些东西,答案是令人惊讶的简单。我还没有意识到UNION可以使用别名在公共列上排序,即使这些列来自不同的表。因此,完整的查询将是:

 
SELECT DISTINCT(e.eventid),n.nameid,n.firstname,n.lastname,d.dt AS dait,r.recurring 
FROM dates d 
LEFT JOIN recurringtypes r 
/* if event recurring every week E.g. 'Every Monday' */ 
ON (r.rectypeid BETWEEN 2 AND 8 AND r.day = d.dow) 
/* if event recurring every month E.g. 'First Monday, every month' */ 
OR ((r.rectypeid BETWEEN 9 AND 36) AND r.day = d.dow AND r.occurrence = d.occurrence) 
/* if event recurring every last week of month E.g. 'Last Monday, every month' */ 
OR (r.rectypeid >= 37 AND r.day = d.dow and r.islast = d.islast) 
LEFT JOIN events e on e.rectypeid = r.rectypeid 
LEFT JOIN eventtypes t ON e.eventtypeid = t.eventtypeid 
LEFT JOIN names n ON e.namesid = n.namesid 
WHERE (d.dt BETWEEN '2012/02/01' AND '2012/05/01') 
UNION 
SELECT e.eventid,n.nameid,n.lastname,n.firstname,e.firstdate AS dait,'No' as Recurring 
FROM events e 
LEFT JOIN names n ON n.names = e.namesid 
AND e.rectypeid <= 1 
WHERE e.firstdate BETWEEN '2012/02/01' AND '2012/05/01' 
ORDER BY dait; 

它已经指出,使用表查找日期是有风险的,因为日期将最终耗尽,这是真实的,但计算一个日期是否是,例如,一个月中的第一个星期一(或者第二个,或者第四个,或者第四个和最后一个),似乎是比现在想要进入的更复杂的SQL代码。

2
SELECT DISTINCT(e.eventid),n.nameid,n.firstname,n.lastname,d.dt,r.recurring 
FROM dates d 
LEFT JOIN recurringtypes r 
/* if event recurring every week E.g. 'Every Monday' */ 
ON (r.rectypeid BETWEEN 2 AND 8 AND r.day = d.dow) 
/* if event recurring every month E.g. 'First Monday, every month' */ 
OR ((r.rectypeid BETWEEN 9 AND 36) AND r.day = d.dow AND r.occurrence = d.occurrence) 
/* if event recurring every last week of month E.g. 'Last Monday, every month' */ 
OR (r.rectypeid >= 37 AND r.day = d.dow and r.islast = d.islast) 
LEFT JOIN events e on e.rectypeid = r.rectypeid OR (e.rectypeid <= 1 AND e.eventid IS NOT NULL) 
LEFT JOIN eventtypes t ON e.eventtypeid = t.eventtypeid 
LEFT JOIN names n ON e.namesid = n.namesid 
WHERE (d.dt BETWEEN '2012/02/01' AND '2012/05/01') 
ORDER BY d.dt; 
+0

辉煌,差不多!这很棒,实际上在那里,但即使在events.rectypeid = 1(没有重复)时,它也会重复行,也许是因为事件和recurringtype表不同步?我怀疑在你的'OR'之后可能会有一个'AND',但我不知道它应该是什么。 – blankabout 2012-02-14 15:03:16

+0

@blankabout:条件是小于或等于,但也许这解决了查询:OR(e.rectypeid <= 1 AND e.eventid IS NOT NULL)。否则,我会尝试e.rectypeid <1?或者你可以添加e.eventid IS NOT NULL到WHERE子句中?这个更便宜。 – Bytemain 2012-02-14 15:10:24

+0

可悲的是这些选项都没有帮助。我想知道一种不同的方法是否会有所帮助,可能是这两种查询的某种联合,尽管我的(有限的)工会经验表明它们只是两个完全独立的查询,并不是特别有用。 – blankabout 2012-02-15 01:56:47