我正在构建一个自定义事件系统,如果您有一个看起来像这样的重复事件:
事件 A 从 2011 年 3 月 3 日开始每 4 天重复一次
要么
事件 B 从 2011 年 3 月 1 日开始,每 2 周在星期二重复一次
如何以一种易于查找的方式将其存储在数据库中。如果有大量事件,我不希望出现性能问题,并且在渲染日历时我必须经过每一个。
对于我的基于 PHP/MySQL 的日历,我想尽可能高效地存储重复/重复事件信息。我不想有大量的行,我想轻松查找在特定日期发生的所有事件。
下面的方法非常适合存储定期出现的重复信息,例如每天、每 n 天、每周、每年每月等。这也包括每周二和周四类型的模式,因为它们被存储每周从周二开始,每周从周四开始。
假设我有两张桌子,一张叫events这样:
events
ID NAME 1 Sample Event 2 Another Event
还有一个events_meta这样的表:
events_meta
ID event_id meta_key meta_value 1 1 repeat_start 1299132000 2 1 repeat_interval_1 432000
repeat_start 是一个没有时间作为 unix 时间戳的日期,而 repeat_interval 是间隔之间的秒数(432000 是 5 天)。
repeat_interval_1 与 ID 1 的 repeat_start 一起使用。因此,如果我有一个每周二和每周四重复的事件,repeat_interval 将是 604800(7 天),并且会有 2 个 repeat_starts 和 2 个 repeat_intervals。该表如下所示:
ID event_id meta_key meta_value 1 1 repeat_start 1298959200 -- This is for the Tuesday repeat 2 1 repeat_interval_1 604800 3 1 repeat_start 1299132000 -- This is for the Thursday repeat 4 1 repeat_interval_3 604800 5 2 repeat_start 1299132000 6 2 repeat_interval_5 1 -- Using 1 as a value gives us an event that only happens once
然后,如果您有一个每天循环的日历,获取当天的事件,查询将如下所示:
SELECT EV.* FROM `events` EV RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id` RIGHT JOIN `events_meta` EM2 ON EM2.`meta_key` = CONCAT( 'repeat_interval_', EM1.`id` ) WHERE EM1.meta_key = 'repeat_start' AND ( ( CASE ( 1299132000 - EM1.`meta_value` ) WHEN 0 THEN 1 ELSE ( 1299132000 - EM1.`meta_value` ) END ) / EM2.`meta_value` ) = 1 LIMIT 0 , 30
替换{current_timestamp}为当前日期的 unix 时间戳(减去时间,因此小时、分钟和秒值将设置为 0)。
{current_timestamp}
希望这对其他人也有帮助!
这种方法更适合存储复杂的模式,例如
Event A repeats every month on the 3rd of the month starting on March 3, 2011
Event A repeats Friday of the 2nd week of the month starting on March 11, 2011
我建议将其与上述系统结合使用以获得最大的灵活性。此表应如下所示:
ID event_id meta_key meta_value 1 1 repeat_start 1299132000 -- March 3rd, 2011 2 1 repeat_year_1 * 3 1 repeat_month_1 * 4 1 repeat_week_im_1 2 5 1 repeat_weekday_1 6
repeat_week_im表示当前月份的第几周,可能介于 1 到 5 之间。repeat_weekday在一周中的一天,1-7。
repeat_week_im
repeat_weekday
现在假设您正在循环通过天/周在日历中创建月视图,您可以编写如下查询:
SELECT EV . * FROM `events` AS EV JOIN `events_meta` EM1 ON EM1.event_id = EV.id AND EM1.meta_key = 'repeat_start' LEFT JOIN `events_meta` EM2 ON EM2.meta_key = CONCAT( 'repeat_year_', EM1.id ) LEFT JOIN `events_meta` EM3 ON EM3.meta_key = CONCAT( 'repeat_month_', EM1.id ) LEFT JOIN `events_meta` EM4 ON EM4.meta_key = CONCAT( 'repeat_week_im_', EM1.id ) LEFT JOIN `events_meta` EM5 ON EM5.meta_key = CONCAT( 'repeat_weekday_', EM1.id ) WHERE ( EM2.meta_value =2011 OR EM2.meta_value = '*' ) AND ( EM3.meta_value =4 OR EM3.meta_value = '*' ) AND ( EM4.meta_value =2 OR EM4.meta_value = '*' ) AND ( EM5.meta_value =6 OR EM5.meta_value = '*' ) AND EM1.meta_value >= {current_timestamp} LIMIT 0 , 30
这与上述方法相结合可以覆盖大多数重复/重复的事件模式。如果我错过了什么,请发表评论。