我检查了几个关于这个主题的老问题,例如:Calendar Recurring/Repeating Events - Best Storage Method但是,答案在性能方面非常糟糕且在实施中很麻烦。从另一个答案可以很容易地说出为什么接受的答案是个坏主意:一个月的事件需要90个查询。这是不可接受的。
无论如何,这是我面临的问题,因此你不会重新阅读这些问题:
我很感激任何帮助!我读了几篇关于这个主题的论文,但仍然无法找到我可以在SQL中专门工作的东西。
答案 0 :(得分:1)
我提出的解决方案是我有一个事件表,其中有五个字段定义事件的重现,如下所述。然后我还有一个计划表,我填充了事件的实际发生。我确实需要一个结束日期,即使他们指定了几年外出的东西,它也是一个月度类型的事件,它不会在日程表中创建那么多条目。
因此,事件存储在事件表中,其中包含startDateTime和endDateTime,如果没有重复,则描述事件的整个持续时间。如果这是一个重复发生的事件,这两个日期时间字段也会定义事件的整体开始和结束。在同一个事件表中,我们有五个定义重复的字段,如下所示。
Schedule表存储每个事件的各个事件。所以它有一个eventId,startDateTime和endDateTime。此开始和结束仅指每次出现,而不是整个跨度。
为查询一段时间内发生的所有计划事件,我只查询计划表,检查是否存在符合此条件的任何事件:
select * from schedule where schedule.startDateTime < @queryPeriodEnd and schedule.endDateTime > @queryPeriodStart
此查询仅向我提供在我的查询期内部分或全部发生的计划条目。要获取事件数据,只需加入事件表即可。
有趣的是计算月份的第二个星期四。这发生在实际代码中,用于计算给定事件的所有计划事件。我也在下面附上我的代码。
事件复发率
复发 0 =没有复发 1 =日常 2 =每周 3 =月
recurs_interval 这是复发之间的多少个时期。如果事件每5天重复一次,则recurs_interval将为5,recurs将为1.如果事件每3周重复一次,则recurs_interval将为3,并且recurs将为2。
recurs_day 如果用户选择每月类型重复,则在该月的某一天(例如:10日或14日)。这个日期。如果用户未选择每月或特定日期重复,则该值为0。否则,该值为1到31。
recurs_ordinal 如果用户选择了每月类型的重复,而是一个序数类型的日(例如:第一个星期一,第二个星期四,上周五)。这将具有该序数。如果用户未选择此类重复,则该值为0。 1 =第一 2 =第二 3 =第三 4 =第四 5 =最后
recurs_weekdays 对于每周和每月 - 有序复发,这将存储复发发生的工作日。 1 =星期日 2 =周一 4 =星期二 8 =星期三 16 =周四 32 =星期五 64 =星期六
所以,周六和周日每4个星期就会这样 recurs = 2,recurs_interval = 4,recurs_weekdays = 65(64 + 1) 同样,每个月的第一个星期五每三个月一次 recurs = 3,recurs_interval = 3,recurs_ordinal = 1,recurs_weekdays = 32
<强> CODE 强>
thisEvent.occurrences = new List<ScheduleInstance>();
DateTime currentDateTime = (DateTime) thisEvent.start;
DateTime currentEndTime;
BitArray WeekDayRecurrenceBits = new BitArray(new Byte[] {(Byte) thisEvent.recursWeekdays});
while (currentDateTime < thisEvent.end)
{
currentEndTime = new DateTime(currentDateTime.Year, currentDateTime.Month, currentDateTime.Day,
thisEvent.end.Value.Hour, thisEvent.end.Value.Minute, thisEvent.end.Value.Second);
switch (thisEvent.recurs)
{
case (RecurrenceTypeEnum.None):
AddOccurrenceToRooms(thisEvent, currentDateTime, currentEndTime);
currentDateTime = (DateTime)thisEvent.end;
break;
case (RecurrenceTypeEnum.Daily):
AddOccurrenceToRooms(thisEvent, currentDateTime, currentEndTime);
currentDateTime = currentDateTime.AddDays(thisEvent.recursInterval);
break;
case (RecurrenceTypeEnum.Weekly):
int indexIntoCurrentWeek = (int) currentDateTime.DayOfWeek;
while ((indexIntoCurrentWeek < 7) && (currentDateTime < thisEvent.end))
{
if (WeekDayRecurrenceBits[(int) currentDateTime.DayOfWeek])
{
AddOccurrenceToRooms(thisEvent, currentDateTime, currentEndTime);
}
currentDateTime = currentDateTime.AddDays(1);
currentEndTime = currentEndTime.AddDays(1);
indexIntoCurrentWeek++;
}
currentDateTime = currentDateTime.AddDays(7 * (thisEvent.recursInterval - 1));
break;
case (RecurrenceTypeEnum.Monthly):
if (thisEvent.recursDay == 0)
{
DateTime FirstOfTheMonth = new DateTime(currentDateTime.Year, currentDateTime.Month, 1);
int daysToScheduleOccurrence = ((thisEvent.recursWeekdays - (int)FirstOfTheMonth.DayOfWeek + 7) % 7)
+ ((thisEvent.recursOrdinal - 1) * 7)
- currentDateTime.Day + 1;
if (daysToScheduleOccurrence >= 0)
{
currentDateTime = currentDateTime.AddDays(daysToScheduleOccurrence);
currentEndTime = currentEndTime.AddDays(daysToScheduleOccurrence);
if (currentDateTime < thisEvent.end)
{
AddOccurrenceToRooms(thisEvent, currentDateTime, currentEndTime);
}
}
}
else
{
if (currentDateTime.Day <= thisEvent.recursDay && thisEvent.recursDay <= DateTime.DaysInMonth(currentDateTime.Year, currentDateTime.Month) )
{
currentDateTime = currentDateTime.AddDays(thisEvent.recursDay - currentDateTime.Day);
currentEndTime = currentEndTime.AddDays(thisEvent.recursDay - currentEndTime.Day);
AddOccurrenceToRooms(thisEvent, currentDateTime, currentEndTime);
}
}
currentDateTime = currentDateTime.AddDays((currentDateTime.Day - 1) * -1).AddMonths(thisEvent.recursInterval);
break;
default:
break;
}
}