我有一个事件系统,对于我的重复事件,我使用的是类似cron的系统。
重复事件:
+----+----------+--------------+
| id | event_id | repeat_value |
+----+----------+--------------+
| 1 | 11 | *_*_* |
| 2 | 12 | *_*_2 |
| 3 | 13 | *_*_4/2 |
| 4 | 14 | 23_*_* |
| 5 | 15 | 30_05_* |
+----+----------+--------------+
注意:cron值为day_month_day of week
事件:
+----+------------------------+---------------------+---------------------+
| id | name | start_date_time | end_date_time |
+----+------------------------+---------------------+---------------------+
| 11 | Repeat daily | 2014-04-30 12:00:00 | 2014-04-30 12:15:00 |
| 12 | Repeat weekly | 2014-05-06 12:00:00 | 2014-05-06 13:00:00 |
| 13 | Repeat every two weeks | 2014-05-08 12:45:00 | 2014-05-08 13:45:00 |
| 14 | Repeat monthly | 2014-05-23 15:15:00 | 2014-05-23 16:00:00 |
| 15 | Repeat yearly | 2014-05-30 07:30:00 | 2014-05-30 10:15:00 |
+----+------------------------+---------------------+---------------------+
无论如何,我有一个查询来选择事件:
SELECT *
FROM RepeatEvent
JOIN `Event`
ON `Event`.`id` = `RepeatEvent`.`event_id`
产生:
+----+----------+--------------+----+------------------------+---------------------+---------------------+
| id | event_id | repeat_value | id | name | start_date_time | end_date_time |
+----+----------+--------------+----+------------------------+---------------------+---------------------+
| 1 | 11 | *_*_* | 11 | Repeat daily | 2014-04-30 12:00:00 | 2014-04-30 12:15:00 |
| 2 | 12 | *_*_2 | 12 | Repeat weekly | 2014-05-06 12:00:00 | 2014-05-06 13:00:00 |
| 3 | 13 | *_*_4/2 | 13 | Repeat every two weeks | 2014-05-08 12:45:00 | 2014-05-08 13:45:00 |
| 4 | 14 | 23_*_* | 14 | Repeat monthly | 2014-05-23 15:15:00 | 2014-05-23 16:00:00 |
| 5 | 15 | 30_05_* | 15 | Repeat yearly | 2014-05-30 07:30:00 | 2014-05-30 10:15:00 |
+----+----------+--------------+----+------------------------+---------------------+---------------------+
但是,我想在一个月内选择活动。我只会有一些条件:每天,每周,每两周,每月和每年。 我想在我的where子句中放入一个方法来划分重复值的字符串,如果它符合以下任何条件以显示它作为结果(repeatEvent是被查询的行,搜索是要查找的日期) :
array(3) = string_divide(repeat_value, '_')
daily = array(0)
monthy = array(1)
dayOfWeek = array(2)
if(daily == '*' && month == '*' && dayOfWeek == '*') //returns all the daily events as they will happen
return repeatEvent
if(if(daily == '*' && month == '*' && dayOfWeek == search.dayOfWeek) //returns all the events on specific day
return repeatEvent
if(daily == search.date && month == '*' && dayOfWeek == '*') //returns all the daily events as they will happen
return repeatEvent
if (contains(dayOfWeek, '/'))
array(2) = string_divide(dayOfWeek,'/')
specificDayOfWeek = array(0);
if(specificDayOfWeek == repeatEvent.start_date.dayNumber)
if(timestampOf(search.timestamp)-timestampOf(repeatEvent.start_date)/604800 == (0 OR EVEN)
return repeatEvent
if(daily == search.date && month == search.month && dayOfWeek == '*') //returns a single yearly event (shouldn't often crop up)
return repeatEvent
//everything else is either an unknown format of repeat_value or not an event on this day
总结一下,我想运行一个查询,其中在where子句中拆分重复值,我可以查询拆分项。我看过游标,但互联网似乎建议反对它们。
我可以处理在PHP中选择所有重复事件的结果,但是,我想这很慢。
如果看一下四月份,我希望看到以下内容:
+----------+--------------+----+------------------------+---------------------+---------------------+
| event_id | repeat_value | id | name | start_date_time | end_date_time |
+----------+--------------+----+------------------------+---------------------+---------------------+
| 11 | *_*_* | 11 | Repeat daily | 2014-04-30 12:00:00 | 2014-04-30 12:15:00 |
+----------+--------------+----+------------------------+---------------------+---------------------+
如果看一下五月,我希望看到的是
+----------+--------------+----+------------------------+---------------------+---------------------+
| event_id | repeat_value | id | name | start_date_time | end_date_time |
+----------+--------------+----+------------------------+---------------------+---------------------+
| 11 | *_*_* | 11 | Repeat daily | 2014-04-30 12:00:00 | 2014-04-30 12:15:00 |
| 12 | *_*_2 | 12 | Repeat weekly | 2014-05-06 12:00:00 | 2014-05-06 13:00:00 |
| 13 | *_*_4/2 | 13 | Repeat every two weeks | 2014-05-08 12:45:00 | 2014-05-08 13:45:00 |
| 14 | 23_*_* | 14 | Repeat monthly | 2014-05-23 15:15:00 | 2014-05-23 16:00:00 |
| 15 | 30_05_* | 15 | Repeat yearly | 2014-05-30 07:30:00 | 2014-05-30 10:15:00 |
+----------+--------------+----+------------------------+---------------------+---------------------+
以下是我希望看到的六月份
+----------+--------------+----+------------------------+---------------------+---------------------+
| event_id | repeat_value | id | name | start_date_time | end_date_time |
+----------+--------------+----+------------------------+---------------------+---------------------+
| 11 | *_*_* | 11 | Repeat daily | 2014-04-30 12:00:00 | 2014-04-30 12:15:00 |
| 12 | *_*_2 | 12 | Repeat weekly | 2014-05-06 12:00:00 | 2014-05-06 13:00:00 |
| 13 | *_*_4/2 | 13 | Repeat every two weeks | 2014-05-08 12:45:00 | 2014-05-08 13:45:00 |
| 14 | 23_*_* | 14 | Repeat monthly | 2014-05-23 15:15:00 | 2014-05-23 16:00:00 |
+----------+--------------+----+------------------------+---------------------+---------------------+
答案 0 :(得分:4)
你可以在上面加上一个绑带,但没有人会帮你告诉你这就是答案。
如果您的MySQL数据库可以更改,我强烈建议您将包含下划线day_month_day of year
的当前列拆分为三个单独的列day
,month
和day_of_year
。我还建议您将格式更改为INT
而不是VARCHAR
。这将使搜索和解析变得更快,更容易,因为它的设计方式不需要通过复杂的程序转换成计算机语言......它已经是大部分方式了。
原因如下:
您的桌子未经过优化,无论您在此阶段选择做什么,都会放慢速度。 SQL不是为了在一列中包含多个值而构建的。 SQL数据库的整个要点是将值拆分为不同的列和行。
规范化此表的优点是搜索它会快得多,并且您将能够在MySQL中构建查询。看看Normalization。这是一个复杂的概念,但一旦你得到它,你将避免创建凌乱和复杂的程序。
计算机根据Unix纪元时间跟踪时间。它计算秒数并始终在您的计算机上运行。实际上,计算机一直在计算它,因为顾名思义,第一台Unix计算机曾经开启过。此外,每个基于计算机和计算机的程序/系统具有内置的快速日期和时间功能。 MySQL也不例外。
我还建议将所有这些存储为整数。 repeat_doy
(一年中的某一天)很容易成为smallint
或至少是一个标准int
,而不是一个月和一天,你可以把实际的1-365天年。您可以使用DAY_OF_YEAR(NOW())
将其输入MySQL。要将其作为日期撤回,您可以使用MAKEDATE(YEAR(NOW),repeat_doy)
。您可以使用0或NULL来代替星号来表示全部。
使用类似cron的系统,无论如何你可能不需要进行那种计算。
相反,在其他地方测量一年中的某一天可能更容易(每台计算机和语言都可以做到这一点。在Unix中它只是date "%j"
)。
将一个repeat_value拆分为三个单独的值,并根据UNIX时间值将它们全部转换为整数。日是1-7(或周日到周六0-6),月是1-12,一年中的日是1-365(记住,我们不包括366因为我们正在以任意非飞跃为基础年)。
如果您想以原始格式提取SELECT
查询中的信息,使用concat
合并三列比尝试搜索和拆分一列更容易。您还可以轻松利用内置的MySQL功能,快速将您所提取的内容转换为真实的,最新的,几天,而无需您付出太多努力。
在SQL数据库中实现它:
+----+----------+--------------+--------------+------------+
| id | event_id | repeat_day | repeat_month | repeat_doy |
+----+----------+--------------+--------------+------------+
| 1 | 11 | * | * | * |
| 2 | 12 | * | * | 2 |
| 3 | 13 | * | * | 4/2 |
| 4 | 14 | 23 | * | * |
| 5 | 15 | 30 | 5 | * |
+----+----------+--------------+--------------+------------+
现在,无论查询的复杂程度如何,您都应该能够构建一个查询来将所有这些数据放在一起。通过规范化您的表格,您将能够充分利用关系数据库的强大功能,而无需头痛和黑客攻击。
修改强>
Hugo Delsing在下面的评论中提到了一个很好的观点。在我最初的例子中,我为day_of_year
提供了一个闰年修复,其中我选择忽略2月29日。一个更好的解决方案消除了修复的需要。使用复合索引将day_of_year
拆分为month
和day
。他还有几周和几周的建议,但我建议你阅读它以获取更多细节。
答案 1 :(得分:2)
尝试用以下方法写出条件:
substring_index(repeat_value,'_', 1)
而不是每天
substring_index(substring_index(repeat_value,'_', -2), '_', 1)
而不是每月 和
substring_index(substring_index(repeat_value,'_', -1), '_', 1)
而不是dayOfWeek
答案 2 :(得分:1)
如果您只想每月而不是每天发生这些事件,我认为您正在过度思考这个问题。假设您始终正确填充repeat_value
,则查询非常基本。
基本上所有事件都发生在repeat_value
为LIKE '%_*_%'
或LIKE '%_{month}_%'
的每个月。
既然你提到PHP
我假设你正在用PHP构建查询,因此我使用了相同的。
<?php
function buildQuery($searchDate) {
//you could/should do some more checking if the date is valid if the user provides the string
$searchDate = empty($searchDate) ? date("Y-m-d") : $searchDate;
$splitDate = explode('-', $searchDate);
$month = $splitDate[1];
//Select everything that started after the searchdate
//the \_ is because else the _ would match any char.
$query = 'SELECT *
FROM RepeatEvent
JOIN `Event`
ON `Event`.`id` = `RepeatEvent`.`event_id`
WHERE `Event`.`start_date_time` < \''.$searchDate.'\'
AND
(
`RepeatEvent`.`repeat_value` LIKE \'%\_'.$month.'\_%\'
OR `RepeatEvent`.`repeat_value` LIKE \'%\_*\_%\'
)
';
return $query;
}
//show querys for all months on current day/year
for ($month = 1; $month<=12; $month++) {
echo buildQuery(date('Y-'.$month.'-d')) . '<hr>';
}
?>
现在,如果repeat_value
可能出错,您可以添加一个简单的正则表达式检查,以确保该值始终为*_*_*
或*_*_*/*
答案 3 :(得分:0)
您可以在MySQL中使用基本正则表达式:
http://dev.mysql.com/doc/refman/5.0/en/pattern-matching.html
对于5月(第一天)的月度活动,您可以使用这样的模式(未经测试):
[0-9\*]+\_[5\*]\_1
您可以通过PHP生成此模式