如何使用可以轻松查询的重新安装事件创建SQL日历?

时间:2015-07-13 19:41:52

标签: mysql postgresql calendar

我检查了几个关于这个主题的老问题,例如:Calendar Recurring/Repeating Events - Best Storage Method但是,答案在性能方面非常糟糕且在实施中很麻烦。从另一个答案可以很容易地说出为什么接受的答案是个坏主意:一个月的事件需要90个查询。这是不可接受的。

无论如何,这是我面临的问题,因此你不会重新阅读这些问题:

  1. 以这种方式存储事件以允许它们重复出现。想想谷歌日历,您可以在其中指定模式,例如"发生在每月的第1天,每个月和#34;或者"每月的第二个星期一发生#34; (后者对我来说不那么重要。
  2. 查询一段时间内的事件列表。例如,我想向某人显示未来2个月的事件列表,而我不想查询该月的每一天。这样做会杀死服务器(每个用户在数千行事件中)
  3. DB不可知论者。我使用PgSQL并在其他论坛上使用MS SQL特定的东西或Oracle看到了很多这个问题的答案。
  4. 我很感激任何帮助!我读了几篇关于这个主题的论文,但仍然无法找到我可以在SQL中专门工作的东西。

1 个答案:

答案 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;
            }
        }