He hecho esto (solo para VEvents, no es compatible con elementos TODO ni entradas del diario ni nada de eso). Mi aplicación es el siguiente (después de quitar las columnas que no son específicos de la pregunta):
-- One table for each event. An event may have multiple rRules.
Create Table [vEvent]
(vEventID Integer Identity(1, 1) Not Null
Constraint [vEvent.pk]
Primary Key
Clustered
,title nVarChar(200) Not Null);
-- One table for rRules.
-- My application does NOT support the "bySetPos" rule, so that is not included.
Create Table [rRule]
(rRuleID Integer Identity(1, 1) Not Null
Constraint [rRule.pk]
Primary Key
Clustered
,vEventID Integer Not Null
Constraint [fk.vEvent.rRules]
Foreign Key
References [vEvent] (vEventID)
On Update Cascade
On Delete Cascade
,[class] varChar( 12) Not Null Default('public')
,[created] DateTime Not Null Default(getUTCDate())
,[description] nVarChar(max) Null
,[dtStart] DateTime Not Null
,[dtEnd] DateTime Null
,[duration] varChar( 20) Null
,[geoLat] Float Null
,[geoLng] Float Null
,[lastModified] DateTime Not Null Default(getUTCDate())
,[location] nVarChar(max) Null
,[organizerCN] nVarChar( 50) Null
,[organizerMailTo] nVarChar(100) Null
,[seq] Integer Not Null Default(0)
,[status] varChar( 9) Not Null Default('confirmed')
,[summary] nVarChar( 75) Null
,[transparent] Bit Not Null Default(0)
,[freq] varChar( 8) Not Null Default('daily')
,[until] DateTime Null
,[count] Integer Null
,[interval] Integer Not Null Default(1)
,[bySecond] varChar(170) Null
,[byMinute] varChar(170) Null
,[byHour] varChar( 61) Null
,[byDay] varChar( 35) Null
,[byMonthDay] varChar(200) Null
,[byYearDay] varChar(3078) Null
,[byWeekNo] varChar(353) Null
,[byMonth] varChar( 29) Null
,[wkSt] Char ( 2) Null Default('mo'));
-- Class must be one of "Confidential", "Private", or "Public"
Alter Table [rRule]
Add Constraint [rRule.ck.Class]
Check ([class] In ('confidential', 'private', 'public'));
-- Start date must come before End date
Alter Table [rRule]
Add Constraint [rRule.ck.dtStart]
Check ([dtEnd] Is Null Or [dtStart] <= [dtEnd]);
-- dtEnd and duration may not both be present
Alter Table [rRule]
Add Constraint [rRule.ck.duration]
Check (Not ([dtEnd] Is Not Null And [duration] Is Not Null));
-- Check valid values for [freq]. Note that 'single' is NOT in the RFC;
-- it is an optimization for my particular iCalendar calculation engine.
-- I use it as a clue that this pattern has only a single date (dtStart),
-- and there is no need to perform extra calculations on it.
Alter Table [rRule]
Add Constraint [rRule.ck.freq]
Check ([freq] In
('yearly'
,'monthly'
,'weekly'
,'daily'
,'hourly'
,'minutely'
,'secondly'
,'single')); -- Single is NOT part of the spec!
-- If there is a latitude, there must be a longitude, and vice versa.
Alter Table [rRule]
Add Constraint [rRule.ck.geo]
Check (([geoLat] Is Null And [geoLng] Is Null)
Or ([geoLat] Is Not Null And [geoLng] Is Not Null));
-- Interval must be positive.
Alter Table [rRule]
Add Constraint [rRule.ck.interval]
Check ([interval] > 0);
-- Status has a set of defined values.
Alter Table [rRule]
Add Constraint [rRule.ck.status]
Check ([status] In ('cancelled', 'confirmed', 'tentative'));
-- Until and Count may not coexist in the same rule.
Alter Table [rRule]
Add Constraint [rRule.ck.until and count]
Check (Not ([until] Is Not Null And [count] Is Not Null));
-- One table for exceptions to rRules. In my application, this covers both
-- exDate and rDate. I do NOT support extended rule logic here; The RFC says
-- you should support the same sort of date calculations here as are supported
-- in rRules: exceptions can recur, etc. I don't do that; mine is simply a
-- set of dates that are either "exceptions" (dates which don't appear, even
-- if the rule otherwise says they should) or "extras" (dates which do appear,
-- even if the rule otherwise wouldn't include them). This has proved
-- sufficient for my application, and something that can be exported into a
-- valid iCalendar file--even if I can't import an iCalendar file that makes
-- use of recurring rules for exceptions to recurring rules.
Create Table [exDate]
(exDateID Integer Identity(1, 1) Not Null
Constraint [exDate.pk]
Primary Key
Clustered
,rRuleID Integer Not Null
Constraint [fk.rRule.exDates]
Foreign Key
References [rRule] (rRuleID)
On Update Cascade
On Delete Cascade
,[date] DateTime Not Null
,[type] varChar(6) Not Null); -- Type = "exDate" or "rDate" for me; YMMV.
Para ir junto con esto, tengo varias funciones CLR de SQL Server 2005 + que se pueden utilizar para calcular las fechas para varios eventos. He encontrado los siguientes formularios para ser muy útil:
Select * From dbo.getDatesByVEventID(@id, @startDate, @endDate)
Select * From dbo.getEventsByDateRange(@startDate, @endDate, @maxCount)
Implementación de lo anterior es muy divertido de descubrir!
Sé que esto es más de 7 años de edad, es esto todavía cómo acercarse a este, Y hace más nueva versión de SQL darle ninguna opción mejor que consultan? –