我正在尝试构建自己的日历,并且我有一个用于输入下面显示的事件的表单.
我不知道如何设计MySQL表以及如何以可以轻松提取的方式记录这些信息?

以下是我的尝试,它有效,但非常难看,我相信它可以改进:
CREATE TABLE events (
event_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
location VARCHAR(100) NOT NULL,
body TEXT NOT NULL,
start_ts DATETIME NOT NULL,
end_ts DATETIME NOT NULL,
valid ENUM('Y','N') DEFAULT 'Y',
reoccurring ENUM('Y','N') DEFAULT 'N',
every ENUM('day','week','month','year',''),
bymonth ENUM('day','weekday',''),
end_date DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (event_id)
);
CREATE TABLE events_byweek (
event_id INTEGER UNSIGNED NOT NULL,
weekday TINYINT UNSIGNED NOT NULL,
FOREIGN KEY (event_id)
REFERENCES events(event_id)
);
-- returns all dates, reoccurring or otherwise within specified time range
-- by month
SELECT * FROM events WHERE (:year = YEAR(start_ts) AND :month = MONTH(start_ts))
OR (reoccurring = 'Y' AND ((YEAR(end_date) >= :year AND MONTH(end_date) >= :month) OR end_date = '0000-00-00 00:00:00')
AND (every != 'year' OR MONTH(start_ts) = :month)) AND valid = 'Y'
-- by day
SELECT * FROM events WHERE DATE(start_ts) = :date
OR (reoccurring = 'Y' AND (DATE(end_date) >= :date OR end_date = '0000-00-00 00:00:00')
AND (every != 'year' OR MONTH(start_ts) = :month)) AND valid = 'Y'
-- by week
SELECT * FROM events_byweek WHERE event_id = :event_id
Run Code Online (Sandbox Code Playgroud)
拜托,我真的很感激!
我认为你不需要任何关系数组.
你的db表可能是:
event_id UINT(10) auto_increment NOT NULL
/* not important fields omitted */
start DATETIME not null
end DATETIME not null
reoccurring ENUM('NO', 'WEEKDAY', 'MONTHDAY', 'MONTH_REL', 'YEARLY') DEFAULT 'NO';
weekdays UINT(1) DEFAULT 0;
until TIMESTAMP DEFAULT NULL
Run Code Online (Sandbox Code Playgroud)
我用DATETIME它,因为它更容易查询(见下文),但事实上它并不重要.TIMESTAMP如果你愿意,你可以保留它.
在weekdays一个可以保持一个字节的位是2^0:星期日,2^1星期一,依此类推.因此,如果事件每天重新发生,你会放在那里127.
如果until是NULL,事件将永远重复.
因为很难通过SQL找到"本月的第3个星期三"是否在特定范围内,我认为即使不是不可能也没有用户定义的功能,但是很难并且代码不清楚,我建议你把你所有的事件,在PHP中取出并过滤掉那里.
只加载必要事件(预过滤)的查询将是:
SELECT ... FROM events
WHERE
/* We take all non-reoccuring events in period */
((reoccurring = 'NO') AND (start >= :start) AND (end <= :end))
OR
/* We take some part of reoccurring events */
((reoccuring <> 'NO') AND ((start <= :end) OR (end >= :start)) AND ((until >= :start) OR until IS NULL)
ORDER BY start ASC;
Run Code Online (Sandbox Code Playgroud)
因此,在获取时,您可以测试记录是否符合以下条件:
如果您的记录不符合标准,请从阵列中删除它,而不是db :-).
一些任务也很容易放入SQL查询中.例如,您可以过滤特定月 - 日的重复事件SELECT ... WHERE ... OR ... (start LIKE '%-:month-:day %)(假设事件的开始和结束相同,如问题中的图片所示).这是该DATETIME领域的一些优点,您可以轻松地搜索它们,就像它们是字符串一样,因此%-12-21 %查找具有第12个月和第21天的所有记录(当然,它们应始终为两位数字).(优点TIMESTAMP是很容易计算日期差异等)
如果事件在每个工作日重复,请使用... LIKE% - % - :day%`,依此类推.
所以最后你需要返回两个函数boolean来检查两个案例:
您甚至可以通过使用蛮力foreach或其他方式对它们进行编码.
此外,如果字段值为127,则无需执行工作日检查 - 因此它每天都会发生.
编辑于2013-03-29 - 解释如何使用位作为工作日
在字段中,weekdays可以将天数保持为位,因为在一个(无符号)INT(1)数中有7天和8位.因此,您不必添加更多列,例如"occurrence _monday","happen_tuesday"等,也不必使用任何关系.我这样做是因为我认为事件可能发生在"每个星期一"和"每个星期五".如果没有,请在那里保留一个数字(0 =星期日,1 =星期一等).
此外,每天发生的事件也是每周7天发生的事件的特例,因此我不需要列中的其他ENUM值reoccurring.
现在如何在PHP中使用它?
您只需要测试是否设置了特定工作日的位.您可以使用按位运算AND符执行此操作.如果你定义一些常量,它会更简单:
define("WEEKDAY_SUNDAY",1); // 2^0
define("WEEKDAY_MONDAY",2); // 2^1
define("WEEKDAY_TUESDAY",4); // 2^2
// ....
define("WEEKDAY_SATURDAY",64); // 2^6
// Does the event occur on Friday, maybe also other weekdays?
if($row['weekdays'] & WEEKDAY_FRIDAY){
// Does the event occurs only on Friday, and no other day?
if($row['weekdays'] == WEEKDAY_FRIDAY){
// Let's make the event occur on Friday and the day(s) that it already occurs
$row['weekdays'] = $row['weekdays'] | WEEKDAY_FRIDAY;
// Make the event occur only on Friday and no other weekday
$row['weekdays'] = WEEKDAY_FRIDAY;
// Let's check if the event occurs today:
if(pow(2,date('w')) & $row['weekdays']){ //...
Run Code Online (Sandbox Code Playgroud)
在date与"W"参数的函数返回一个从0到6天的周数,所以这就是为什么我用它如此.
| 归档时间: |
|
| 查看次数: |
1525 次 |
| 最近记录: |