2012-02-14 9 views
6

반복되는 이벤트에 대한 질문은 공통적이지만 캘린더 앱 이외의 반복되는 이벤트에 대해서는이 특정 질문에 대한 답변으로 찾을 수 없었습니다. 가장 큰 차이점은 앱의 이벤트입니다. 캘린더와 관련된 수하물의 수가 적을지라도 여러면에서 매우 유사하지만 캘린더 형식이 아닌 보고서 또는 자체적으로 만 볼 수 있습니다.되풀이 이벤트, SQL 쿼리

캘린더 앱과 비슷한 방식으로 이벤트는 일회성으로 발생하거나 반복 될 수 있습니다. 매주 목요일 또는 매월 첫 번째 월요일, 미리 설정된 시간까지.

이벤트는 시작 및 종료 날짜와 '재귀 유형 ID'를 포함하는 이벤트 테이블에 저장됩니다. 'recurrency type'이 'None'이면 시작일과 종료일은 동일합니다. 이벤트 테이블은 이벤트 유형 이름을 포함하는 별도의 테이블에 ID를 보유합니다 (예 : '회의'또는 '주간 보고서'

'재주문 유형'목록이 포함 된 추가 표가 있습니다. '재발 없음', '매주 월요일', '매월 첫째 월요일'및 '매월 마지막 토요일'입니다.

조회를 쉽게하기 위해 다른 표에는 1960에서 2060까지의 날짜 목록과 각 날짜에 대한 관련 정보 (예 : 월요일인지, 어떤 달의 월요일인지 등)가 들어 있습니다.

허용 조회와 같은 :
 
+-----------+---------------+-------------------+-----------+------------+-------------------------------+ 
| 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 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; 

 
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; 
이것은 첫 번째 쿼리와 매우 유사한 출력을 제공하지만 중요하게는 날짜가 dates 테이블이 아닌 events 테이블에서 나온 것입니다.

내 질문은 : 어떻게 모든 날짜와 시간 순서로 반복되는 모든 이벤트를 포함하는 하나의 목록을 만들기 위해 이러한 쿼리를 결합 할 수 있습니까?


이들은은 테이블과 그들로부터 단축 선택, 일부 열하고 모든 인덱스는 : 간결 제거되었습니다. '이름'테이블은 같은 이유로 포함되지 않았습니다.

 
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'는 무엇을합니까? 또는'dates' 테이블에'occurence'가 있습니까? – Naltharial

+0

'islast'를 설정하면 한 달에 마지막으로 발생한 일 (예 : 지난 월요일의 월)을 표시하고, occurrence은 한 달의 요일 (예 : '첫 번째 달의 월', 두 번째 월요일의 월 ') – blankabout

답변

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

눈부시게, 거의! 이벤트와 recurringtype 테이블이 동기화되지 않기 때문에 events.rectypeid = 1 (반복 없음) 일지라도 행이 반복됩니다. 나는 당신의 'OR'다음에 'AND'가 누락 된 것으로 의심하지만 그것이 무엇인지 생각할 수 없다. – blankabout

+0

@blankabout : 조건이 작거나 같지만 쿼리를 해결할 수 있습니다. OR (e.rectypeid <= 1 AND e.eventid IS NOT NULL). 그렇지 않으면 e.rectypeid <1을 시도할까요? 또는 e.eventid가 WHERE 절에 NULL이 아닌 것을 추가 할 수 있습니까? 더 싸다. – Bytemain

+0

슬프게도 그러한 옵션 중 어느 것도 도움이되지 않았습니다. 필자의 (제한된) 노조 경험에 비추어 볼 때 두 가지 완전히 다른 쿼리가 특별히 유용한 방식으로 함께 용접되지는 않았지만 다른 접근 방식이 도움이되는지 궁금하다. – blankabout