针对每周默认计划的最佳MySQL设置,其中包含备用计划

时间:2013-09-14 21:21:53

标签: php mysql schedule

再次问好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设置吗?

2 个答案:

答案 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