我正在构建一个自定义事件系统,如果你有一个重复的事件,看起来像这样:
事件A从2011年3月3日开始每4天重复一次
or
赛事B从2011年3月1日开始,每两周在周二举行一次
我如何将其存储在数据库中,使其易于查找。如果有大量的事件,我不希望出现性能问题,而且在呈现日历时,我必须遍历每一个事件。
我正在构建一个自定义事件系统,如果你有一个重复的事件,看起来像这样:
事件A从2011年3月3日开始每4天重复一次
or
赛事B从2011年3月1日开始,每两周在周二举行一次
我如何将其存储在数据库中,使其易于查找。如果有大量的事件,我不希望出现性能问题,而且在呈现日历时,我必须遍历每一个事件。
当前回答
为什么不使用类似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)]
希望这能有所帮助。
其他回答
为什么不使用类似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)]
希望这能有所帮助。
@Rogue编码器
这太棒了!
你可以简单地使用模运算(mysql中的MOD或%)让你的代码在最后变得简单:
而不是:
AND (
( CASE ( 1299132000 - EM1.`meta_value` )
WHEN 0
THEN 1
ELSE ( 1299132000 - EM1.`meta_value` )
END
) / EM2.`meta_value`
) = 1
Do:
$current_timestamp = 1299132000 ;
AND ( ('$current_timestamp' - EM1.`meta_value` ) MOD EM2.`meta_value`) = 1")
为了更进一步,我们可以把那些不会永远重复的事件也包括进来。
可以添加“repeat_interval_1_end”之类的内容,以表示最后一个“repeat_interval_1”的日期。然而,这使得查询更复杂,我真的不知道如何做到这一点…
也许有人可以帮忙!
我专门为这种情况开发了一种深奥的编程语言。它最好的部分是它是无模式的和平台独立的。你只需要为你的时间表写一个选择器程序,它的语法受到这里描述的一组规则的约束
https://github.com/tusharmath/sheql/wiki/Rules
这些规则是可扩展的,您可以根据想要执行的重复逻辑类型添加任何类型的自定义,而不用担心模式迁移等问题。
这是一种完全不同的方法,它本身可能有一些缺点。
对于所有对此感兴趣的人,现在你可以在几分钟内复制粘贴开始学习。我尽可能地采纳了评论中的建议。如果我遗漏了什么,请告诉我。
“复杂版”:
事件
+----------+----------------+ | ID | NAME | +----------+----------------+ | 1 | Sample event 1 | | 2 | Second event | | 3 | Third event | +----------+----------------+
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示例代码:
<?
// ----------------------------------------------------------------------------------------------------
// 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 设置这个需要几分钟。没有时间。:)
增强:用日期替换时间戳
作为对公认答案的一个小改进,随后由ahoffner改进-可以使用日期格式而不是时间戳。优点是:
数据库中可读的日期 年份> 2038和时间戳没有问题 删除时需要小心基于季节性调整日期的时间戳,即在英国,6月28日比12月28日早一个小时开始,因此从日期中获得时间戳可能会破坏递归算法。
要做到这一点,将DB repeat_start更改为'date'类型,并且repeat_interval现在保存天数而不是秒。即重复7天。
修改SQL行:
WHERE (( 1370563200 - repeat_start) % repeat_interval = 0 )
to:
WHERE ( DATEDIFF( '2013-6-7', repeat_start ) % repeat_interval = 0)
其他一切都保持不变。简单的!