小编典典

日历重复/重复事件 - 最佳存储方法

all

我正在构建一个自定义事件系统,如果您有一个看起来像这样的重复事件:

事件 A 从 2011 年 3 月 3 日开始每 4 天重复一次

要么

事件 B 从 2011 年 3 月 1 日开始,每 2 周在星期二重复一次

如何以一种易于查找的方式将其存储在数据库中。如果有大量事件,我不希望出现性能问题,并且在渲染日历时我必须经过每一个。


阅读 92

收藏
2022-04-04

共1个答案

小编典典

存储“简单”重复模式

对于我的基于 PHP/MySQL 的日历,我想尽可能高效地存储重复/重复事件信息。我不想有大量的行,我想轻松查找在特定日期发生的所有事件。

下面的方法非常适合存储定期出现的重复信息,例如每天、每 n
天、每周、每年每月等。这也包括每周二和周四类型的模式,因为它们被存储每周从周二开始,每周从周四开始。

假设我有两张桌子,一张叫events这样:

ID    NAME
1     Sample Event
2     Another Event

还有一个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)。

希望这对其他人也有帮助!


存储“复杂”的重复模式

这种方法更适合存储复杂的模式,例如

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    NAME
1     Sample Event
2     Another Event

还有一个events_meta这样的表:

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。

现在假设您正在循环通过天/周在日历中创建月视图,您可以编写如下查询:

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

这与上述方法相结合可以覆盖大多数重复/重复的事件模式。如果我错过了什么,请发表评论。

2022-04-04