再次问好Stackoverflow!
在过去的几天里,我一直在考虑采用适当的方法来保存每周一小时的时间表。
这是时间表:
Monday:
00:00 - 08:00: Sleeping
08:00 - 09:00: Shower & Breakfast
09:00 - 17:00: Work
17:00 - 18:00: Groceries
18:00 - 20:00: Making & Eating dinner
20:00 - 22:00: Relaxing
22:00 - 23:00: Shower & prepare for bed
23:00 - 24:00: Sleeping
这是星期一一直到周日,当然每天都有不同的时间和不同的事情。并且在本周末,如果即将到来的一周没有不同的时间表,那很好,但是如果第34周有不同的时间表,那么用户可以采取当前时间表,保持调整并“安排”仅限第34周的新计划。
正如我所说,在过去的几天里,我一直在考虑为此设置MySQL的正确方法,但我没想到。那么你们能帮我想一下这个适当的MySQL设置吗?
答案 0 :(得分:2)
也许这会让事情开始:
days_of_week
+----+-------------------+
| id | name |
+----+-------------------+
| 1 | Monday |
| 2 | Tuesday |
| 3 | Wednesday |
| 4 | Thursday |
| 5 | Friday |
| 6 | Satday |
| 7 | Sunday |
+----+-------------------+
users
+----+-------------------+-----
| id | name | ...
+----+-------------------+-----
| 1 | John | ...
+----+-------------------+-----
schedules
+----+---------+-------------------+------------+
| id | user_id | name | is_default |
+----+---------+-------------------+------------+
| 1 | 1 | Weekly Default | Y |
| 2 | 1 | Vacation | N |
+----+---------+-------------------+------------+
schedule_details
(NULL in day_of_week_id is the schedule for any day that is not explicitly set)
+----+-------------+----------------+-----------+-----------+--------------------------+
| id | schedule_id | day_of_week_id | from_time | thru_time | description |
+----+-------------+----------------+-----------+-----------+--------------------------+
| 1 | 1 | NULL | 00:00 | 08:00 | Sleeping |
| 2 | 1 | NULL | 08:00 | 09:00 | Shower & Breakfast |
| 3 | 1 | NULL | 09:00 | 17:00 | Work |
| 4 | 1 | NULL | 17:00 | 18:00 | Groceries |
| 5 | 1 | NULL | 18:00 | 20:00 | Making & Eating dinner |
| 6 | 1 | NULL | 20:00 | 22:00 | Relaxing |
| 7 | 1 | NULL | 22:00 | 23:00 | Shower & prepare for bed |
| 8 | 1 | NULL | 23:00 | 24:00 | Sleeping |
| 9 | 1 | 6 | 00:00 | 10:00 | Sleeping |
| 10 | 1 | 6 | 10:00 | 11:00 | Shower & Breakfast |
| 11 | 1 | 6 | 11:00 | 17:00 | Play Golf |
| 12 | 1 | 6 | 17:00 | 18:00 | Groceries |
| 13 | 1 | 6 | 18:00 | 20:00 | Making & Eating dinner |
| 14 | 1 | 6 | 20:00 | 22:00 | Relaxing |
| 15 | 1 | 6 | 22:00 | 23:00 | Shower & prepare for bed |
| 16 | 1 | 6 | 23:00 | 24:00 | Sleeping |
| 17 | 1 | 7 | 00:00 | 10:00 | Sleeping |
| 18 | 1 | 7 | 10:00 | 11:00 | Shower & Breakfast |
| 19 | 1 | 7 | 11:00 | 17:00 | Go Sailing |
| 20 | 1 | 7 | 17:00 | 18:00 | Groceries |
| 21 | 1 | 7 | 18:00 | 20:00 | Making & Eating dinner |
| 22 | 1 | 7 | 20:00 | 22:00 | Relaxing |
| 23 | 1 | 7 | 22:00 | 23:00 | Shower & prepare for bed |
| 24 | 1 | 7 | 23:00 | 24:00 | Sleeping |
| 25 | 2 | NULL | 00:00 | 10:00 | Sleeping |
| 26 | 2 | NULL | 10:00 | 11:00 | Shower & Breakfast |
| 27 | 2 | NULL | 11:00 | 17:00 | Play Golf |
| 28 | 2 | NULL | 18:00 | 20:00 | Go out to dinner |
| 29 | 2 | NULL | 20:00 | 22:00 | Relaxing |
| 30 | 2 | NULL | 22:00 | 23:00 | Shower & prepare for bed |
| 31 | 2 | NULL | 23:00 | 24:00 | Sleeping |
+----+-------------+----------------+-----------+-----------+--------------------------+
weekly_schedules
+----+---------+---------+-------------+
| id | user_id | week_no | schedule_id |
+----+---------+---------+-------------+
| 1 | 1 | 34 | 2 |
+----+---------+---------+-------------+
选择每周时间表:
SELECT dow.id
,dow.name
,sd.from_time
,sd.thru_time
,sd.description
FROM days_of_week dow
JOIN users u
ON u.id = :user_id
LEFT OUTER
JOIN weekly_schedules ws
ON ws.user_id = u.id
AND ws.week_no = :week_no
JOIN schedules s
ON s.user_id = u.id
AND ( (ws.week_no IS NULL AND s.is_default = 'Y')
OR (ws.week_no IS NOT NULL AND s.id = ws.schedule_id)
)
LEFT OUTER
JOIN (SELECT DISTINCT schedule_id, day_of_week_id
FROM schedules ss
JOIN schedule_details sds
ON ss.user_id = :user_id
AND sds.schedule_id = ss.id
AND sds.day_of_week_id IS NOT NULL
) sdow
ON sdow.schedule_id = s.id
AND sdow.day_of_week_id = dow.id
JOIN schedule_details sd
ON sd.schedule_id = s.id
AND ( (sdow.day_of_week_id IS NOT NULL AND sd.day_of_week_id = sdow.day_of_week_id)
OR (sdow.day_of_week_id IS NULL AND sd.day_of_week_id IS NULL)
)
ORDER BY dow.id, sd.from_time
SQLFiddle:http://sqlfiddle.com/#!2/7fc91/8通过将AND ws.week_no = 22
更改为AND ws.week_no = 34
来对其进行测试。
用户可以拥有任意数量的“计划”,其中一个必须是,并且只有一个可以是默认计划。默认计划用于没有明确计划覆盖的任何周。
每个时间表可以包含任意数量的schedule_details,用于标识一周内要进行的活动。
每个计划的详细信息可以包括一天的默认日期(在day_of_week_id列中由NULL标识),并包含当天的任意数量的活动。任何未明确定义的日期都将使用默认日期的计划。
<强>更新强>
如果您希望能够保留历史时间表,那么您可能希望将默认时间表生效。删除schedules.is_default列并将其替换为另一个表:
schedule_defaults
+---------+------------+-------------+
| user_id |schedule_id | eff_week_no |
+---------+------------+-------------+
| 1 | 1 | 18 |
+---------+------------+-------------+
然后,相应地调整SELECT。
SELECT u.name
,dow.id
,dow.name
,sd.from_time
,sd.thru_time
,sd.description
FROM days_of_week dow
JOIN users u
ON u.id = :user_id
LEFT OUTER
JOIN weekly_schedules ws
ON ws.user_id = u.id
AND ws.week_no = :week_no
JOIN schedule_defaults sdef
ON sdef.user_id = u.id
AND sdef.eff_week_no = (SELECT MAX(eff_week_no)
FROM schedule_defaults
WHERE user_id = :user_id
AND eff_week_no <= :week_no
)
JOIN schedules s
ON s.user_id = u.id
AND ( (ws.week_no IS NULL AND s.id = sdef.schedule_id)
OR (ws.week_no IS NOT NULL AND s.id = ws.schedule_id)
)
LEFT OUTER
JOIN (SELECT DISTINCT schedule_id, day_of_week_id
FROM schedules ss
JOIN schedule_details sds
ON ss.user_id = :user_id
AND sds.schedule_id = ss.id
AND sds.day_of_week_id IS NOT NULL
) sdow
ON sdow.schedule_id = s.id
AND sdow.day_of_week_id = dow.id
JOIN schedule_details sd
ON sd.schedule_id = s.id
AND ( (sdow.day_of_week_id IS NOT NULL AND sd.day_of_week_id = sdow.day_of_week_id)
OR (sdow.day_of_week_id IS NULL AND sd.day_of_week_id IS NULL)
)
ORDER BY dow.id, sd.from_time
用于保留历史记录的SQLFiddle:http://sqlfiddle.com/#!2/e721c/10
答案 1 :(得分:0)
我认为你的问题没有'最好'的答案。我给你一些架构,但可能会有更多的答案也会很好。
shedule_map schedule activity
------------------------------------------------
id id id
user_id schedule_id schedule_id
default_schedule_id user_id date
startOfWeek start_hour
end_hour
activity_description
每周'Schedule'表每个用户将获得一个条目。如果用户在“计划”表中选择当前周的默认计划,则schedule_id将从schedule_map获取。 Elswhere schedule_id = MAX(schedule_id)+ 1,将创建此新计划的新活动。让我们从user_id = 10的模式中获取今天的活动:
SELECT act.start_hour, act.end_hour, act.activity_descripton
FROM activity act
INNER JOIN schedule s
USING (schedule_id)
WHERE s.startOfWeek = (
SELECT MAX(startOfWeek)
FROM schedule
WHERE startOfWeek < NOW()
)
AND s.user_id = 10
ORDER BY act.start_hour
我主要使用ORM并且子查询存在问题,因此startOfWeek足以满足您从其他查询中获取的当前日期。
我认为每个用户每周只能输入一个额外的条目。但是如果你认为那么你可以添加'schedule_map'表列'custom_schedule_id',如果用户选择默认计划,则将其设置为NULL。但是,您必须首先确定用户是否使用自定义计划,否则将需要更复杂的查询。例如
SELECT act.start_hour, act.end_hour, act.activity_descripton
FROM activity act
INNER JOIN schedule s
ON act.schedule_id = s.schedule_id
INNER JOIN schedule_map sm
ON sm.user_id = s.user_id
AND sm.custom_schedule_id = s.schedule_id
WHERE s.user_id = 10
ORDER BY act.start_hour
UNION ALL
SELECT act.start_hour, act.end_hour, act.activity_descripton
FROM activity act
INNER JOIN schedule s
ON act.schedule_id = s.schedule_id
INNER JOIN schedule_map sm
ON sm.user_id = s.user_id
AND sm.default_schedule_id = s.schedule_id
AND sm.custom_schedule_id IS NULL
WHERE s.user_id = 10
ORDER BY act.start_hour