2012-02-14 20 views
6

Sono consapevole che le domande su eventi ricorrenti sono comuni ma non sono riuscito a trovarne una con una risposta a questa domanda specifica su eventi ricorrenti diversi da quelli relativi alle app di calendario. La differenza principale è che gli eventi nella nostra app. sarà sempre visto solo nei report o da solo piuttosto che in un formato di calendario anche se in molti modi sono molto simili, forse solo con meno del bagaglio associato ai calendari.Eventi ricorrenti, query SQL

In modo simile a un'app di calendario. gli eventi possono verificarsi su base singola o possono essere ricorrenti. ogni giovedì o primo lunedì di ogni mese, fino a qualche ora prestabilita in futuro.

Gli eventi sono memorizzati in una tabella eventi che contiene le date di inizio e di fine e l''id del tipo di ricorrenza'. Se il "tipo di ricorrenza" è "Nessuno", le date di inizio e di fine saranno le stesse. La tabella degli eventi contiene un id per una tabella separata che contiene il nome del tipo di evento, ad es. "Riunione" o "Rapporto settimanale"

C'è una ulteriore tabella che contiene l'elenco dei "tipi di ricorrenza", ad es. "Nessuna ricorrenza", "Ogni lunedì", "Primo lunedì del mese" e "Ultimo sabato del mese".

Per semplificare le ricerche, un'altra tabella contiene un elenco di date dal 1960 al 2060 insieme a informazioni pertinenti su ciascuna data, ad esempio se si tratta di un lunedì e quale occorrenza del lunedì è nel mese.

questo permette una ricerca come:

 
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; 

Che è esattamente ciò che è necessario per la ricerca di eventi ricorrenti, dando un output simile:

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

Per ottenere nessuno eventi ricorrenti di una query più semplice può essere utilizzato:

 
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; 
Questo dà un output molto simile alla prima query ma, soprattutto, le date sono dalla tabella degli eventi piuttosto che dalla tabella delle date.

La mia domanda è: come posso combinare queste query per trovare una lista che contenga tutti gli eventi, sia ricorrenti che non ricorrenti in ordine di data?


Queste sono le tabelle e le selezioni accorciato da loro, alcune colonne e tutti gli indici sono stati rimossi per brevità :). La tabella 'nomi' non è stata inclusa per lo stesso motivo.

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


Non siamo assolutamente impostato sulla utilizzando il codice di cui sopra o il layout tavolo, eventuali soluzioni di lavoro sarebbe il benvenuto. Per favore, non puntare verso:

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?

o

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

li ho controllato e sono stati molto utili, ma non fare la lo stesso che intendiamo.

TIA

+0

Cosa fa "islast' do? O "occorrenza" nella tabella "date"? – Naltharial

+0

Se impostato 'islast' contrassegna l'ultima occorrenza di un giorno in un mese (ad esempio 'ultimo lunedì del mese), l'occorrenza è il conteggio delle occorrenze di un giorno in un mese (ad esempio' primo lunedì del mese ',' secondo lunedì in mese ') – blankabout

risposta

2

A meno che non mi manca qualcosa la risposta è sorprendentemente semplice. Non mi ero reso conto che le UNION possono essere ordinate su colonne comuni usando un alias, anche se quelle colonne provengono da tabelle diverse. Quindi la domanda completa sarebbe:

 
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; 

E 'stato sottolineato che l'uso di un tavolo per la ricerca di date è un rischio perché le date alla fine esaurirsi, il che è vero, ma calcolando se una data è, per esempio, il primo lunedì di un mese (o il secondo, o il quarto o forse il quarto e l'ultimo), sembra un po 'più complesso di codice SQL di quello che voglio ottenere al momento.

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

Brillante, quasi! È grandioso ed è praticamente lì, ma ripete le righe anche quando eventi.rectypeid = 1 (nessuna ricorrenza), forse perché le tabelle evento e di tipo ricorrente scadono la sincronizzazione? Sospetto che potrebbe esserci un "E" mancante dopo il tuo "OR", ma non riesco a pensare a cosa dovrebbe essere. – blankabout

+0

@blankabout: la condizione è minore o uguale ma forse questo risolve la query: OR (e.rectypeid <= 1 AND e.eventid NON È NULL). Altrimenti proverò e.rectypeid <1? O puoi aggiungere e.eventid IS NOT NULL alla clausola WHERE? È più economico. – Bytemain

+0

Purtroppo nessuna delle due opzioni ha aiutato. Mi chiedo se un approccio diverso potrebbe aiutare, forse una sorta di unione delle due domande, anche se la mia (limitata) esperienza dei sindacati suggerisce che sono solo due query completamente separate saldate insieme in un modo non particolarmente utile. – blankabout