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


311

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

从2011年3月3日开始,活动A每4天重复一次

要么

从2011年3月1日开始,活动B每2周重复一次

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


你能解释为什么1299132000 要硬编码吗?如果我需要获取给定结束日期的发生日期和用户,该怎么办?
Murali Murugesan 2013年

@Murali Boy这是旧的,但是我很确定1299132000应该是当前日期。
Brandon Wamboldt 2013年

@BrandonWamboldt,我尝试了使用SQL Server的想法。stackoverflow.com/questions/20286332/display-next-event-date。我想找到所有下一个项目,例如c#版本
Billa 2013年

Answers:


211

存储“简单”重复模式

对于基于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

可以与上述方法结合使用,以覆盖大多数重复/重复事件模式。如果我错过了任何事情,请发表评论。


1
我正在尝试存储“简单”重复模式。如果我需要在每周的星期二重复一次,是否需要修改repeat_start或创建具有最后日期的新记录。还是有一种方法可以根据第一个repeat_start每周重复一次????
loo 2012年

1
你的答案是一个很大的帮助@roguecoder,但它并没有完全工作了蝙蝠...我发现后,这个帖子我的回答:stackoverflow.com/questions/10545869/...
本·辛克莱尔

1
AND ( ( CASE ( 1299132000 - EM1.meta_value ) WHEN 0 THEN 1 ELSE ( 1299132000 - EM1.meta_value) END ) / EM2.meta_value ) = 1/ EM2.meta_value放置错误吗?
Murali Murugesan

1
这是一个很大的帮助。如果您想对单个事件发表评论或签到,您将如何建议将它们作为单个记录访问?
johnrees 2014年

26
值得注意的是,您不应将硬编码的值用于重复间隔(即86400一天中的几秒钟),因为它不考虑夏令时。动态地计算这些东西并存储interval = dailyand interval_count = 1interval = monthlyand 更合适interval_count = 1
Corey Ballou 2014年

185

尽管当前接受的答案对我有很大帮助,但我想分享一些有用的修改,以简化查询并提高性能。


“简单”重复事件

处理定期发生的事件,例如:

Repeat every other day 

要么

Repeat every week on Tuesday 

您应该创建两个表,一个表events如下所示:

ID    NAME
1     Sample Event
2     Another Event

events_meta像这样的表:

ID    event_id      repeat_start       repeat_interval
1     1             1369008000         604800            -- Repeats every Monday after May 20th 2013
1     1             1369008000         604800            -- Also repeats every Friday after May 20th 2013

由于repeat_start是没有时间的unix时间戳日期(1369008000对应于2013年5月20日),并且repeat_interval间隔之间的秒数(604800是7天)。

通过遍历日历中的每一天,您可以使用以下简单查询获得重复事件:

SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE  (( 1299736800 - repeat_start) % repeat_interval = 0 )

只需用unix-timestamp(1299736800)替换日历中的每个日期即可。

注意模数(%符号)的使用。该符号类似于常规除法,但返回“余数”而不是商,因此,当当前日期是repeat_start的repeat_interval的精确倍数时,该符号为0。

性能比较

这比以前建议的基于“ meta_keys”的答案要快得多,答案如下:

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

如果运行EXPLAIN此查询,您会注意到它需要使用连接缓冲区:

+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref              | rows | Extra                          |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
|  1 | SIMPLE      | EM1   | ALL    | NULL          | NULL    | NULL    | NULL             |    2 | Using where                    |
|  1 | SIMPLE      | EV    | eq_ref | PRIMARY       | PRIMARY | 4       | bcs.EM1.event_id |    1 |                                |
|  1 | SIMPLE      | EM2   | ALL    | NULL          | NULL    | NULL    | NULL             |    2 | Using where; Using join buffer |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+

上面具有1个连接的解决方案不需要此类缓冲区。


“复杂”模式

您可以添加对更复杂类型的支持,以支持以下类型的重复规则:

Event A repeats every month on the 3rd of the month starting on March 3, 2011

要么

Event A repeats second Friday of the month starting on March 11, 2011

您的事件表可以看起来完全一样:

ID    NAME
1     Sample Event
2     Another Event

然后,要添加对这些复杂规则的支持,请添加列,events_meta如下所示:

ID    event_id      repeat_start       repeat_interval    repeat_year    repeat_month    repeat_day    repeat_week    repeat_weekday
1     1             1369008000         604800             NULL           NULL            NULL          NULL           NULL             -- Repeats every Monday after May 20, 2013
1     1             1368144000         604800             NULL           NULL            NULL          NULL           NULL             -- Repeats every Friday after May 10, 2013
2     2             1369008000         NULL               2013           *               *             2              5                -- Repeats on Friday of the 2nd week in every month    

请注意,您只需要么指定一个repeat_interval 一组repeat_yearrepeat_monthrepeat_dayrepeat_week,和repeat_weekday数据。

这使得同时选择两种类型非常简单。只需遍历每一天并填写正确的值即可(2013年6月7日为1370563200,然后是年,月,日,周号和工作日,如下所示):

SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE  (( 1370563200 - repeat_start) % repeat_interval = 0 )
  OR ( 
    (repeat_year = 2013 OR repeat_year = '*' )
    AND
    (repeat_month = 6 OR repeat_month = '*' )
    AND
    (repeat_day = 7 OR repeat_day = '*' )
    AND
    (repeat_week = 2 OR repeat_week = '*' )
    AND
    (repeat_weekday = 5 OR repeat_weekday = '*' )
    AND repeat_start <= 1370563200
  )

这将返回在第二周的星期五重复的所有事件,以及在每个星期五重复的所有事件,因此它将返回事件ID 1和2:

ID    NAME
1     Sample Event
2     Another Event

*在上面的SQL中,我使用PHP Date的默认工作日索引作为旁注,因此星期五为“ 5”


希望这对其他人的帮助与原始答案对我的帮助一样大!


6
太神奇了,谢谢!您是否知道如何编码“第一个星期一每2个月”或“第一个星期一每3个月”等?
约旦列夫

6
我同意这是惊人的。但是,我遇到了与乔丹·列夫相同的困境。repeat_interval字段不能重复几个月,因为某些月份比其他月份更长。此外,您如何限制重复事件的持续时间。即,每两个月的第一个星期一为8个月。该表应具有某种结束日期。
Abinadi

11
这是一个很好的答案。我删除了repeat_interval并添加了repeat_end日期,但是这个答案有很大帮助。
伊恩·柯林斯

3
提示:对于复杂的模式,可以消除该repeat_interval列并在随后的列中表示(repeat_year例如,等等)。对于第一行,可以通过在2013年5月20日之后的每个星期一重复的情况来表示repeat_weekday*在其他列。
musubi

3
@OlivierMATROT @milos的想法是将要固定的字段设置为显式,其余字段设置为通配符*。因此,对于“ 3月的每个月”,您只需将其设置repeat_day为3,将其余repeat字段设置为*(保留为repeat_intervalnull),然后将repeat_start设置为2011年3月3日的unix时间代码作为您的定位日期。
ahoffner '19

28

增强功能:将时间戳记替换为日期

作为对后来被ahoffner完善的答案的一个小增强-可以使用日期格式而不是时间戳。优点是:

  1. 数据库中的可读日期
  2. > 2038年和时间戳都没有问题
  3. 删除时需要谨慎,因为时间戳是基于季节性调整后的日期得出的,即在英国,6月28日比12月28日早了1个小时,因此从日期中获取时间戳可能会破坏递归算法。

为此,请更改数据库repeat_start以将其存储为“日期”类型,并repeat_interval保留天数而不是秒数。即7重复7天。

更改sql行:

WHERE (( 1370563200 - repeat_start) % repeat_interval = 0 )

至:

WHERE ( DATEDIFF( '2013-6-7', repeat_start ) % repeat_interval = 0)

其他一切都保持不变。简单!


那如果我希望我的活动逐年重复怎么办?repeat_interval应该存储365天?如果他们一年有366天怎么办?
TGeorge'3

3
@ George02如果事件为年度,则将repeat_interval留为NULL,repeat_year为*,然后根据重复发生的情况,您可以将repeat_month和repeat_day设置为3月11日,或将repeat_month,repeat_week和repeat_weekday设置为4月的第二个星期二。
jerrygarciuh,2015年

25

我将遵循此指南:https : //github.com/bmoeskau/Extensible/blob/master/recurrence-overview.md

还要确保使用iCal格式,以免重新发明轮子,并记住规则0: 请勿将单个重复事件实例存储为数据库中的行!


2
您将如何为参加过特定实例的跟踪用户建模?在这种情况下,打破规则0是否有意义?
丹尼·沙利文

2
@DannySullivan在我的头顶上,我将拥有另一个attendedEvent带有baseInstanceId和的实体instanceStartDate-例如,您从中创建了重复规则日历视图并使用开始日期来指定该特定实例上的信息的基础事件,那么该实体也可以有这样的东西attendedListId导致另一个表idattendedUserId
Gal Bracha

@DannySullivan我知道你问了已经有一段时间了。但是在上一条注释之外,您始终可以进行反向查找以查看该用户是否属于事件重复模式。这样可以告诉您是否至少已安排好参加活动。他们是否真的参加了另一个不同的故事,这更像是DannySullivan的评论。
布罗格斯

24

对于所有对此感兴趣的人,现在您只需几分钟即可复制并粘贴上手。我尽我所能接受了建议。让我知道我是否想念一些东西。

“复杂版本”:

大事记

+ ---------- + ---------------- +
| ID | NAME |
+ ---------- + ---------------- +
| 1 | 样本事件1 |
| 2 | 第二事件
| 3 | 第三事件
+ ---------- + ---------------- +

events_meta

+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
| ID | event_id | repeat_start | repeat_interval | repeat_year | repeat_month | repeat_day | repeat_week | repeat_weekday |
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
| 1 | 1 | 2014-07-04 | 7 | NULL | NULL | NULL | NULL | NULL |
| 2 | 2 | 2014-06-26 | NULL | 2014 | * | * | 2 | 5 |
| 3 | 3 | 2014-07-04 | NULL | * | * | * | * | 5 |
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +

SQL代码:

CREATE TABLE IF NOT EXISTS `events` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `NAME` varchar(255) NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;

--
-- Dumping data for table `events`
--

INSERT INTO `events` (`ID`, `NAME`) VALUES
(1, 'Sample event'),
(2, 'Another event'),
(3, 'Third event...');

CREATE TABLE IF NOT EXISTS `events_meta` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `event_id` int(11) NOT NULL,
  `repeat_start` date NOT NULL,
  `repeat_interval` varchar(255) NOT NULL,
  `repeat_year` varchar(255) NOT NULL,
  `repeat_month` varchar(255) NOT NULL,
  `repeat_day` varchar(255) NOT NULL,
  `repeat_week` varchar(255) NOT NULL,
  `repeat_weekday` varchar(255) NOT NULL,
  PRIMARY KEY (`ID`),
  UNIQUE KEY `ID` (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;

--
-- Dumping data for table `events_meta`
--

INSERT INTO `events_meta` (`ID`, `event_id`, `repeat_start`, `repeat_interval`, `repeat_year`, `repeat_month`, `repeat_day`, `repeat_week`, `repeat_weekday`) VALUES
(1, 1, '2014-07-04', '7', 'NULL', 'NULL', 'NULL', 'NULL', 'NULL'),
(2, 2, '2014-06-26', 'NULL', '2014', '*', '*', '2', '5'),
(3, 3, '2014-07-04', 'NULL', '*', '*', '*', '*', '1');

也可以作为MySQL导出(易于访问)

PHP示例代码index.php:

<?php
    require 'connect.php';    

    $now = strtotime("yesterday");

    $pushToFirst = -11;
    for($i = $pushToFirst; $i < $pushToFirst+30; $i++)
    {
        $now = strtotime("+".$i." day");
        $year = date("Y", $now);
        $month = date("m", $now);
        $day = date("d", $now);
        $nowString = $year . "-" . $month . "-" . $day;
        $week = (int) ((date('d', $now) - 1) / 7) + 1;
        $weekday = date("N", $now);

        echo $nowString . "<br />";
        echo $week . " " . $weekday . "<br />";



        $sql = "SELECT EV.*
                FROM `events` EV
                RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
                WHERE ( DATEDIFF( '$nowString', repeat_start ) % repeat_interval = 0 )
                OR ( 
                    (repeat_year = $year OR repeat_year = '*' )
                    AND
                    (repeat_month = $month OR repeat_month = '*' )
                    AND
                    (repeat_day = $day OR repeat_day = '*' )
                    AND
                    (repeat_week = $week OR repeat_week = '*' )
                    AND
                    (repeat_weekday = $weekday OR repeat_weekday = '*' )
                    AND repeat_start <= DATE('$nowString')
                )";
        foreach ($dbConnect->query($sql) as $row) {
            print $row['ID'] . "\t";
            print $row['NAME'] . "<br />";
        }

        echo "<br /><br /><br />";
    }
?>

PHP示例代码connect.php:

<?
// ----------------------------------------------------------------------------------------------------
//                                       Connecting to database
// ----------------------------------------------------------------------------------------------------
// Database variables
$username = "";
$password = "";
$hostname = ""; 
$database = ""; 

// Try to connect to database and set charset to UTF8
try {
    $dbConnect = new PDO("mysql:host=$hostname;dbname=$database;charset=utf8", $username, $password);
    $dbConnect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

} catch(PDOException $e) {
    echo 'ERROR: ' . $e->getMessage();
}
// ----------------------------------------------------------------------------------------------------
//                                      / Connecting to database
// ----------------------------------------------------------------------------------------------------
?>

另外,这里还提供了php代码(以提高可读性):
index.php

connect.php
现在进行设置应该花费您几分钟。不是几个小时。:)


2
我如何查询以获取日期范围内的所有重复事件..即获取2014-10-01至2014-12-30之间的所有重复事件。感谢您的发布
Wisher Wisher 2014年

@Wellwisher -重复......直到和临时表 stackoverflow.com/questions/34407833/...
布拉德·肯特

1
@Alex如何从重复事件中删除一个实例实例。
Pugazhenthi 2015年

1
我知道这是一个旧线程,但是为什么repeat_ *列上的varchar类型呢?您不能使用整数和负值而不是'*'吗?
奥利维尔·马特

1
感谢您的代码。但是,我必须指出,您的数据库/查询实现有点令人不安,并且效率很低。例如,为什么要对此类简单列使用varchar(255)(如@OlivierMATROT所述,您可以使用整数,即使不是,也可以使用255?)。如果要重复查询30次,为什么不使用语句或过程呢?只是说说是否有人要实现这一点。
罗尼

15

当建议的解决方案工作时,我尝试使用完整日历来实现,并且每个视图都需要进行90多次数据库调用(因为它加载了当前,上个月和下个月),对此我并不感到兴奋。

我发现了一个递归库https://github.com/tplaner/当您将规则简单地存储在数据库中以及一个查询以提取所有相关规则时。

希望这对其他人有帮助,因为我花了很多时间试图找到一个好的解决方案。

编辑:此库是为PHP


我也想使用fullcalendar。图书馆什么时候可以帮助我?如何提取发起人事件?
Piernik

@piernik-我将像文档中那样设置库,如果遇到特定问题,请使用已设置的代码和所遇到的问题在stackoverflow上打开一个新问题。我敢肯定,如果您在某些成员上付出很大的努力会有所帮助。
蒂姆·拉姆西

我的意思是,使用WhenYou必须将所有递归日期存储在数据库中,或者获取所有递归事件并在数据库中的php no中生成日期。我对吗?
Piernik

@piernik您会将初始日期和规则存储在数据库中,并用于When生成所有日期-这些日期是从初始存储的日期/规则中填充的。
蒂姆·拉姆西

这也不好-您无法在单个mysql命令中获得正确的事件-您必须使用PHP。无论如何还是要感谢
Piernik '16

14

为什么不使用类似于Apache cron作业的机制?http://en.wikipedia.org/wiki/Cron

对于日历\计划,我将为“位”使用略有不同的值来容纳标准的日历重现事件-代替[星期几(0-7),月份(1-12),月份(1-31),小时(0-23),分钟(0-59)]

-我会使用[年(每N年重复一次),月份(1-12),月份中的某天(1-31),月份中的某周(1-5),一周中的某日(0-7) ]

希望这可以帮助。


6
我认为这是一周中太多的选择。1-7或0-6似乎更准确。
Abinadi

2
最好使用cron来存储重复项。但问题是查找非常困难。
Stony 2014年

@Vladimir您如何存储,每两周二(周二每两周)
julestruong

@julestruong这个网站看起来像答案一样:coderwall.com/p/yzzu5a/running-a-cron-job-every-other-week
Ashton Wiersdorf

cron具有有限的表示性,因为它是无状态的(仅将当前/次要日期/时间与模式进行比较),因此,它不能表示某些常见的业务/人为模式,例如“每隔三天”或“每隔7小时”,需要记住最后一次发生。这并不明显;您可能会认为您只是在crontab中说了day / 3或hour / 7,但是在月/日结束时,您的“剩余”天/小时数少于3或7;可能带来灾难性的结果。
海梅·格雷罗

5

我为这种情况开发了一种深奥的编程语言。关于它的最好的部分是它较少架构且与平台无关。您只需要编写一个选择器程序即可,因为您的日程安排的语法受此处描述的规则集约束-

https://github.com/tusharmath/sheql/wiki/规则

这些规则是可扩展的,您可以根据要执行的重复逻辑的种类添加任何类型的自定义,而无需担心架构迁移等。

这是一种完全不同的方法,可能有其自身的一些缺点。


4

听起来很像存储在系统表中的MySQL事件。您可以查看结构并找出不需要的列:

   EVENT_CATALOG: NULL
    EVENT_SCHEMA: myschema
      EVENT_NAME: e_store_ts
         DEFINER: jon@ghidora
      EVENT_BODY: SQL
EVENT_DEFINITION: INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP())
      EVENT_TYPE: RECURRING
      EXECUTE_AT: NULL
  INTERVAL_VALUE: 5
  INTERVAL_FIELD: SECOND
        SQL_MODE: NULL
          STARTS: 0000-00-00 00:00:00
            ENDS: 0000-00-00 00:00:00
          STATUS: ENABLED
   ON_COMPLETION: NOT PRESERVE
         CREATED: 2006-02-09 22:36:06
    LAST_ALTERED: 2006-02-09 22:36:06
   LAST_EXECUTED: NULL
   EVENT_COMMENT:


3

@流氓编码器

这很棒!

您可以简单地使用模运算(在mysql中为MOD或%)来使代码最后变得简单:

代替:

AND (
    ( CASE ( 1299132000 - EM1.`meta_value` )
        WHEN 0
          THEN 1
        ELSE ( 1299132000 - EM1.`meta_value` )
      END
    ) / EM2.`meta_value`
) = 1

做:

$current_timestamp = 1299132000 ;

AND ( ('$current_timestamp' - EM1.`meta_value` ) MOD EM2.`meta_value`) = 1")

为了进一步做到这一点,可以包括不会永远发生的事件。

可以添加诸如“ repeat_interval_1_end”之类的来表示最后一个“ repeat_interval_1”的日期。但是,这使查询更加复杂,我无法真正弄清楚该怎么做...

也许有人可以帮忙!


1

您所给出的两个例子非常简单。它们可以表示为一个简单的时间间隔(第一天是四天,第二天是14天)。如何建模将完全取决于您的重复操作的复杂性。如果以上内容确实如此简单,请在重复间隔中存储开始日期和天数。

但是,如果您需要支持诸如

从2011年3月3日开始,活动A在每月的3号每月重复一次

要么

从2011年3月11日开始,活动A重复该月的第二个星期五

那是一个更复杂的模式。


1
我添加了您刚才所说但现在还没有说过的更复杂的规则。我将如何对SQL查询进行建模以获取例如2011年3月7日的事件,以便获取我的重复事件?
Brandon Wamboldt
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.