我必须实体:postes
和 bookings
。 poste
和 booking
之间存在一对多关系:一个 poste 可能有多个预订(在不同的日期)。
bookings
由 4 列定义:
booking_id
: idposte_id
:合并postes
表start_datetime
: 预订开始日期number_day
: 天数(整数)
postes
由 4 列定义:
poste_id
: poste idpattern
(字符串):定义了允许的日期(1 是允许的,0 是不允许的)。第 8 天定义为模式的第 1 天(模 7)start
:发布开始日期(预订中的所有日期都包含在开始和结束之间)end
:发布结束日期
目标:我想定义一个查询来选择所有未完全保留的postes
(例如,一些新的保留是可能的)。我被卡住了,因为我无法为免费日期范围选择任何数据,因为只存储了预订。
示例
Booking table
| booking_id | poste_id | start_datetime | number_day |
|------------|----------|----------------------|------------|
| 1 | 1 | 2019-07-10T00:00:00Z | 4 |
| 4 | 1 | 2019-07-14T00:00:00Z | 1 |
| 7 | 1 | 2019-07-16T00:00:00Z | 4 |
| 2 | 2 | 2019-07-10T00:00:00Z | 2 |
| 9 | 2 | 2019-07-13T00:00:00Z | 2 |
| 5 | 3 | 2019-07-15T00:00:00Z | 2 |
| 8 | 3 | 2019-07-21T00:00:00Z | 3 |
| 11 | 3 | 2019-07-28T00:00:00Z | 1 |
| 12 | 3 | 2019-07-29T00:00:00Z | 1 |
| 3 | 4 | 2019-07-15T00:00:00Z | 1 |
| 13 | 4 | 2019-07-21T00:00:00Z | 2 |
Postes table:
| poste_id | pattern | start | end |
|----------|---------|----------------------|----------------------|
| 1 | 1111101 | 2019-07-10T00:00:00Z | 2019-07-20T00:00:00Z |
| 2 | 1101101 | 2019-07-10T00:00:00Z | 2019-07-20T00:00:00Z |
| 3 | 1100001 | 2019-07-15T00:00:00Z | 2019-07-30T00:00:00Z |
| 4 | 1011001 | 2019-07-15T00:00:00Z | 2019-07-30T00:00:00Z |
该示例的预期输出是:2,4
。 (邮局 1
和 3
不再有预订)。
注意:
- 这是一个简单的例子。实际上,日期范围更大,例如几个月。
- 数据库假设预订不重叠。
- 模式长度可能不同于 7。模式的第一天与一周中的天数之间没有联系。例如,如果模式为“1101”且开始日期为“10-07-2019”,则表示第 10、11、13、14、15、17 等天可用,其他天不可用。<
- 预订日期始终在
poste
的开始日期和结束日期之间。
可重复性:
// Build the tables:
CREATE TABLE bookings
(`booking_id` int, `poste_id` int, `start_datetime` datetime, `number_day` int)
;
INSERT INTO bookings
(`booking_id`, `poste_id`, `start_datetime`, `number_day`)
VALUES
(1, 1, '2019-07-10', '4'),
(4, 1, '2019-07-14', '1'),
(7, 1, '2019-07-16', '4'),
(2, 2, '2019-07-10', '2'),
(9, 2, '2019-07-13', '2'),
(5, 3, '2019-07-15', '2'),
(8, 3, '2019-07-21', '3'),
(11, 3, '2019-07-28', '1'),
(12, 3, '2019-07-29', '1'),
(3, 4, '2019-07-15', '1'),
(13, 4, '2019-07-21', '2')
;
CREATE TABLE postes
(`poste_id` int, `pattern` VARCHAR(7), `start` datetime, `end` datetime);
INSERT INTO postes VALUES
(1, "1111101", "2019-07-10", "2019-07-20"),
(2, "1101101", "2019-07-10", "2019-07-20"),
(3, "1100001", "2019-07-15", "2019-07-30"),
(4, "1011001", "2019-07-15", "2019-07-30");
我的工作:到目前为止,我设法找到给定日期的可用帖子:
SELECT DISTINCT p.*
FROM postes p
LEFT JOIN bookings b
ON b.poste_id = p.poste_id
WHERE
/* Ignore date in past */
MOD(DATEDIFF("2019-07-16", p.start), LENGTH(p.pattern)) >= -1
AND
/* Filter poste with pattern = 1 */
SUBSTRING(p.pattern, MOD(DATEDIFF("2019-07-16", p.start),
LENGTH(p.pattern)) + 1 , 1) = 1
AND
/* Filter those available this day */
p.poste_id NOT IN (
SELECT b.poste_id
FROM bookings b
WHERE b.start_datetime <= "2019-07-16"
AND "2019-07-16" < DATE_ADD(b.start_datetime, INTERVAL b.number_day DAY)
);
输出:
| poste_id | pattern | start | end |
|----------|---------|----------------------|----------------------|
| 2 | 1101101 | 2019-07-10T00:00:00Z | 2019-07-20T00:00:00Z |
最佳答案
(还不是一个完整的答案,但至少有一些提示......)
bookings
的第 2 列是poste_id
还是room_id
? (我想“room”更适合英语??)- 使用
DATE
数据类型代替DATETIME
。 (此外,MySQL 会在日期时间文字中的T
和Z
上阻塞。) - 使用
TINYINT UNSIGNED
而不是VARCHAR(7)
。这将允许您使用 bool 运算、移位运算和BIT_COUNT()
函数。 (参见 https://dev.mysql.com/doc/refman/8.0/en/bit-functions.html。)这些应该有助于进行所需的计算。 - 你的位串有 7 长,好像它与星期几有关。但是吗?也就是说,第一位是否与星期日相关?还是与
poste.start
相关? - 您使用的是什么版本的 MySQL?在 8.0 之前,位操作限制为 64,因此将位操作解决方案限制为大约 2 个月。使用 8.0,操作的大小几乎是无限的。
所以,对于 8.0,我可能会
- 将位(非字符)模式复制足够次数。 (嗯……
REPEAT
很容易用于字符,但不是位。也许用 char 字符串重复,然后转换为位。) - 在
结束
日期之后砍掉一些位。 BIT_COUNT()
查看范围内有多少天可用。SUM(number_day)
获取预留天数。- 减去看看有多少天没有保留。 (注意:这假定数据是“有效的”,即
bookings
中没有任何“重叠”。
(我可能会按照 Aprillion 的建议用“真正的”编程语言编写代码。我上面的步骤可能在那里有用。)
对于旧版本的 MySQL,和/或使用 VARCHAR(7)
而不是 TINYINT
,上述步骤可能有效,但需要进行一些替换。例如,BIT_COUNT
可以替换为 LENGTH(s) - LENGTH(REPLACE(s, '1', ''))
关于mysql - 如何知道一组日期范围是否涵盖更大的日期范围?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56998802/