我正在开发一个律师预约系统,一个人可以在给定日期的给定时间(下一个律师有空的日子)预约。
假设它是针对律师的 ZocDoc。相同的结构,基于时间的约会:http: //goo.gl/djUZb
我正在使用 MySQL 和 PHP。
表架构:
CREATE TABLE `laywer_appointments` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`lawyer_id` INT unsigned,
`day_of_week` tinyint(3) unsigned DEFAULT '1',
`slot_date` date DEFAULT NULL,
`slot_time` time DEFAULT NULL,
`status` tinyint(4) NOT NULL DEFAULT '0',
`client_id` int(11) DEFAULT NULL, -- client_id = NULL means free slot
);
第 1 点)
每个律师都有基于星期几的默认时间段(状态 = 0 表示可用)。插入默认插槽时,我不提供日期,只提供 day_of_week。示例数据:
+-----------+-------------+-----------+-----------+
| lawyer_id | day_of_week | slot_time | status |
+-----------+-------------+-----------+-----------+
| 1 | 1 | 08:00 | 0 |
| 1 | 1 | 08:30 | 0 |
| 1 | 1 | 09:00 | 0 |
| 1 | 1 | 10:30 | 0 |
| 1 | 4 | 14:30 | 0 |
| 1 | 4 | 16:40 | 0 |
| 2 | 1 | 10:20 | 0 |
| 2 | 1 | 14:00 | 0 |
| 2 | 3 | 15:50 | 0 |
+-----------+-------------+-----------+-----------+
第 2 点)
律师可以为特定日期添加时间段(即使这一天与他的默认时间段不同),也可以锁定(状态=-1)特定日期的默认时间段之一(即他正在开会或生病):
+-----------+-------------+-----------+-----------+-----------+
| lawyer_id | day_of_week | slot_time | slot_date | status |
+-----------+-------------+-----------+-----------+-----------+
| 1 | 1 | 16:00 | 12/03/13 | 0 |
| 1 | 6 | 11:00 | 26/04/13 | 0 |
| 1 | 6 | 12:00 | 26/04/13 | 0 |
| 2 | 1 | 10:00 | 01/01/13 | -1 |
+-----------+-------------+-----------+-----------+-----------+
第 3 点)
然后我们预约了。在这种情况下,我们填写 slot_date 和 client_id:
+-----------+-------------+-----------+-----------+-----------+
| lawyer_id | day_of_week | slot_time | slot_date | client_id |
+-----------+-------------+-----------+-----------+-----------+
| 1 | 1 | 10:30 | 12/03/13 | 10 |
+-----------+-------------+-----------+-----------+-----------+
例如,通过上述预订并假设仍然是同一天(13 年 12 月 3 日)的 6:30,必须打印的可用空位为:
8:00 - default slot
8:30 - default slot
9:00 - default slot
16:00 - Specific slot inserted in point 2 for 12/03/13
问题:
我必须返回下一个可用日期和相关的空闲时间(默认的、特定的减去锁定的和预订的)。我不能只说“从 2013 年 10 月 10 日星期一开始的返回时间”。
在搜索结果页面中,我将列出所有律师以及每个. 因此,这意味着每次进行搜索时,每位律师都会有不同的时间表。
我不能简单地说“SELECT time FROM [bunch of joins] WHERE date = today”。
我提出了这个查询,它忽略了锁定(状态 = -1)或预订(client_id 不为空)的插槽,但当然它不会返回最近一天的空闲时间以及可用时间(或从今天开始):
SELECT p.day_of_week, p.slot_date, p.slot_time
FROM laywer_appointments p
WHERE p.client_id IS NULL AND p.status = 0
AND p.slot_time NOT IN (
SELECT s.slot_time FROM laywer_appointments s
WHERE (s.slot_date IS NOT NULL AND s.client_id IS NOT NULL
OR s.status = -1) AND s.day_of_week = p.day_of_week
)
GROUP BY p.day_of_week, p.slot_date, p.slot_time
ORDER BY p.day_of_week ASC, p.slot_time ASC;
另一个问题:如果今天是 day_of_week = 5,但给定律师的下一个可用 day_of_week 是 2,我该如何查询?
如何返回下一个最接近且可用的 day_of_week 并聚合为从这一天开始的返回时间,而不是所有天?
一种可能的解决方案
我带来的一件事是创建 3 个表而不是一个:
- default_slots:3 列:lawyer_id、day_of_week、time
- 插槽:laywer_id、day_of_week、时间、日期、状态
- 约会:有关预约的所有信息
然后,我会将实际日期的每一天的所有空闲时间段存储在每个律师的时间段表中,最多一年。(取自 default_slots 的时隙)。
+-----------+-------------+-----------+-----------+-----------+
| lawyer_id | day_of_week | slot_time | slot_date | status |
+-----------+-------------+-----------+-----------+-----------+
| 1 | 1 | 16:00 | 12/03/13 | 0 |
| 1 | 1 | 16:00 | 12/03/13 | 0 |
| 1 | 2 | 08:00 | 13/03/13 | 0 |
| 1 | 2 | 09:00 | 13/03/13 | 0 |
... next week
| 1 | 1 | 16:00 | 19/03/13 | 0 |
| 1 | 1 | 16:00 | 19/03/13 | 0 |
| 1 | 2 | 08:00 | 20/03/13 | 0 |
| 1 | 2 | 09:00 | 20/03/13 | 0 |
... up to an year
| 1 | 1 | 16:00 | 20/03/14 | 0 |
| 1 | 1 | 16:00 | 20/03/14 | 0 |
| 1 | 2 | 08:00 | 21/03/14 | 0 |
| 1 | 2 | 09:00 | 21/03/14 | 0 |
+-----------+-------------+-----------+-----------+-----------+
我还将有一些每周运行的 cron 作业,在表槽中添加另一周的空闲槽记录,并删除过去的记录以减少表大小和未使用的数据。
律师还可以将时间直接锁定到插槽中,以及添加特定时间(第 2 点)。
对于列表,这将是获得等于或大于今天的空闲时间的日期的插槽的问题,因为每个日期的每个时间都会有一行。
对这个解决方案的影响: 1)第一天我们将有 2500 名律师(第二个月大约 6000 名)。假设 8 个可能的时段/每天 X 20 天工作/月 X 12 个月 =每位律师 1920 条时段记录。
2500 层 x 1920 条记录 = 第一天的 480 万条记录。(第二个月~12M)
这些记录将一直被更新、插入和删除。slot 表有一些索引,所以我无法想象在一个有 12M+ 记录和一些索引的表上不断进行写操作。每秒更新的索引对我来说并不明智。
我真的无法提供合理且可扩展的解决方案。我的解决方案只有一张表可以工作,但我根本想不出查询的方式。并且非规范化的槽表将是巨大的,同时需要不断的写入操作。
有小费吗?