多分これは物事を始めるでしょう:
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/8AND ws.week_no = 22
に変更してテストしAND ws.week_no = 34
ます。
ユーザーは任意の数の「スケジュール」を持つことができ、そのうちの 1 つが必要であり、1 つだけが既定のスケジュールになることができます。デフォルトのスケジュールは、明示的なスケジュールのオーバーライドがない週に使用されます。
各スケジュールには、1 週間を通して実行されるアクティビティを識別する、任意の数の schedule_details を含めることができます。
各スケジュールの詳細には、1 つのデフォルト日 (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