我有一张叫预约的桌子和一张叫轮班的桌子.在预约表中,有shift_id,所以它看起来像这样:

id | start_date | end_date | shift_id ( FK )

例如,班次为09:00至17:00,其中包含两个约会:

09:30 - 10:10 | 11:30 - 11:50 | 13:00 - 14:00

我目前正在使用这个查询来获取可用的时间段,但它有一个边缘 case 问题.它找不到从9-9:30开始的时间.

SELECT Available_from, Available_to
        FROM (
        SELECT @lasttime_to AS Available_from, start_date AS Available_to, @lasttime_to := end_date
        FROM (SELECT start_date, end_date
               FROM appointments
                WHERE shift_id = " . $shiftData->id . "
                AND end_date <= '" . $shiftData->end_date->toDateTimeString() . "'
                AND start_date >= '" . $shiftData->start_date->toDateTimeString() . "'
               UNION ALL
             SELECT '". $shiftData->start_date->toDateTimeString() ."', '". $shiftData->start_date->toDateTimeString() ."'
                   ORDER BY end_date 
        
             ) e
        JOIN (SELECT @lasttime_to := NULL) init) x
        WHERE Available_to > DATE_ADD(Available_from, INTERVAL " . $serviceDurations . " MINUTE)

这里面少了什么?我怎样才能开始显示在查询中?

http://sqlfiddle.com/#!9/e5292d/2

推荐答案

首先,解决你的原始SQL(在小提琴中).

对于第一行,@lasttime_to为空,会导致问题.

对于空情况(第一行),需要有条件地使用"移位开始时间".

另一种方法见LAG.这正是@variable黑客试图取代的,在MySQL中提供LAG之前.

试试这个:

SELECT Available_from, Available_to
  FROM (
    SELECT COALESCE(@lasttime_to, '2022-03-15 10:00:00') AS Available_from, start_date AS Available_to, @lasttime_to := end_date
    FROM (SELECT start_date, end_date
             FROM appointments
             WHERE shift_id = 4600
             AND end_date <= '2022-03-15 17:00:00'
             AND start_date >= '2022-03-15 10:00:00'
             UNION ALL
               SELECT '2022-03-15 17:00:00', '2022-03-15 17:00:00'
               ORDER BY start_date 
           ) e
    JOIN (SELECT @lasttime_to := NULL) init) x
    WHERE Available_to > DATE_ADD(Available_from, INTERVAL 20 MINUTE)
;

结果是:

Available_from Available_to
2022-03-15 10:00:00 2022-03-15 10:30:00
2022-03-15 12:15:00 2022-03-15 15:00:00
2022-03-15 15:40:00 2022-03-15 17:00:00

这里还有一些窗口功能.我重新安排了逻辑,以避免与轮班开始/结束细节相关的多个神奇常数.然而,我并没有消除所有的魔法.

我仍然更喜欢我删除的更具活力的方法.

WITH shift (shift_start, shift_end) AS (
       SELECT '2022-03-15 10:00:00', '2022-03-15 17:00:00'
     )
   , e0 AS (
       SELECT shift_id, start_date, end_date
         FROM appointments
        WHERE shift_id = 4600
          AND end_date   <= (SELECT shift_end   FROM shift)
          AND start_date >= (SELECT shift_start FROM shift)
        UNION ALL
       SELECT 4600, shift_end, shift_end FROM shift
        ORDER BY start_date 
     )
   , e AS (
      SELECT e0.*
           , LAG(end_date) OVER (PARTITION BY shift_id ORDER BY start_date) AS lasttime_to
        FROM e0
     )
SELECT shift_id, Available_from, Available_to
  FROM (
       SELECT shift_id
            , CAST(COALESCE(lasttime_to, shift_start) AS datetime) AS Available_from
            , start_date AS Available_to
         FROM e
         CROSS JOIN shift
      ) x
 WHERE Available_to > DATE_ADD(Available_from, INTERVAL 20 MINUTE)
;

稍有不同的版本:

WITH e0 AS (
       SELECT a.shift_id, a.start_date, a.end_date
            , s.start_date AS shift_start
            , s.end_date   AS shift_end
         FROM appointments AS a
         JOIN shifts       AS s
           ON a.shift_id = 4600
          AND a.shift_id = s.id
          AND a.end_date   <= s.end_date
          AND a.start_date >= s.start_date
        UNION
       SELECT id, end_date, end_date
            , start_date  , end_date
         FROM shifts WHERE id = 4600
     )
   , e AS (
      SELECT e0.*
           , LAG(end_date) OVER (PARTITION BY shift_id ORDER BY start_date) AS lasttime_to
        FROM e0
     )
SELECT shift_id, Available_from, Available_to
  FROM (
       SELECT shift_id
            , CAST(COALESCE(lasttime_to, shift_start) AS datetime) AS Available_from
            , start_date AS Available_to
         FROM e
      ) x
 WHERE Available_to > DATE_ADD(Available_from, INTERVAL 20 MINUTE)
;

The fiddle

Mysql相关问答推荐

是否有一种方法可以在mysql中设置一列,使其自动成为该行的行号的值?

在mySQL中使用子查询和WHERE子句的JOIN

MySQL滑动窗口动态间隔?

MySQL中如何对字符串进行算术运算?

Mysql:使用like Select 查询

如何确保 SQL 记录的列不引用它自己的主键?

从 MySQL WordPress Select 最后一行

Select 并统计所有条目并根据条目对它们进行分组

状态为已发货的订单返回产品总数量的SQL(不同的两张表)

如何在 SQL 中计算留存曲线?

MYSQL 删除两个已知字符串之间的字符串

在表中找到最大值,然后分别显示SQL组和每个SQL组中的最大值计数

如何使用同一个表在 2 个字段上左连接

是否可以在一个查询中将字符串附加到许多匹配的行

使用带有 ELSEIF 和 ELSE 的 3 列更新问题

如何将表的链接列转换为 SQL 中的行?

加快 MySQL 中的行计数

MySql 以秒为单位的两个时间戳之间的差异?

字符ي和ی以及波斯语的区别 - Mysql

MySQL更新查询与左连接和分组依据