mysql - 如何知道一组日期范围是否涵盖更大的日期范围?

标签 mysql sql bit-manipulation

我必须实体:postesbookingspostebooking 之间存在一对多关系:一个 poste 可能有多个预订(在不同的日期)。

bookings 由 4 列定义:

  • booking_id: id
  • poste_id:合并postes
  • start_datetime: 预订开始日期
  • number_day: 天数(整数)

postes 由 4 列定义:

  • poste_id: poste id
  • pattern(字符串):定义了允许的日期(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。 (邮局 13 不再有预订)。

注意:

  • 这是一个简单的例子。实际上,日期范围更大,例如几个月。
  • 数据库假设预订不重叠
  • 模式长度可能不同于 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 会在日期时间文字中的 TZ 上阻塞。)
  • 使用 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,我可能会

  1. 将位(非字符)模式复制足够次数。 (嗯……REPEAT 很容易用于字符,但不是位。也许用 char 字符串重复,然后转换为位。)
  2. 结束 日期之后砍掉一些位。
  3. BIT_COUNT() 查看范围内有多少天可用。
  4. SUM(number_day) 获取预留天数。
  5. 减去看看有多少天没有保留。 (注意:这假定数据是“有效的”,即 bookings 中没有任何“重叠”。

(我可能会按照 Aprillion 的建议用“真正的”编程语言编写代码。我上面的步骤可能在那里有用。)

对于旧版本的 MySQL,和/或使用 VARCHAR(7) 而不是 TINYINT,上述步骤可能有效,但需要进行一些替换。例如,BIT_COUNT 可以替换为 LENGTH(s) - LENGTH(REPLACE(s, '1', ''))

关于mysql - 如何知道一组日期范围是否涵盖更大的日期范围?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56998802/

相关文章:

php - 超链接强制文件下载 - 如何在服务器上保存文件?

php - 如何在不遍历整个数据库的情况下获得最佳匹配?

php 订阅者 sql 查询太大?

c++ - GCC 位扫描向前查找下一个设置位?

mysql - 使用 MySQL 从单个表中的借方和贷方计算余额

php - 无需提交即可使用Jquery/Ajax即时更新Mysql表

php - 如何检测表中的冗余行?

php - store 函数不将数据保存到数据库

c++ - 两个整数的异或可以超出界限吗?

c - 位移位和整数提升?