SQL中的岛屿与间隙(一天中的时间点)

编程语言 2026-07-09

我们正在努力让在工作中预订外出用车这件事变得更容易一些。如今我们按1小时的时段来创建,但如果有人想把其中一个时段用整整一天,他们需要多次重复预订流程。

我想要做的是创建一个车辆可用的时段,并允许人们按任意时长来选择并预订这辆车。

基于这个想法,我想要一些SQL来识别车辆尚未被预订的时段,以便把这些“可用时段”呈现给想要用车的用户。

我的会话会有一个from和 to日期,预订也会有相同的字段。我之前试着理解这个“岛屿场景”,但在这里有点吃力。

DECLARE @BookingSession TABLE
(
    id_BookingSession INT
    ,SessionStart DATETIME
    ,SessionEnd DATETIME
);

INSERT INTO @BookingSession (id_BookingSession,SessionStart,SessionEnd)
VALUES
(
    1,'20260507 09:00','20260507 17:00'
);

DECLARE @Bookings TABLE
(
    id_BookingSession INT
    ,id_BookingSession_Slot INT
    ,TimeFrom DATETIME
    ,TimeUntil DATETIME
);

--Make some bookings:
INSERT INTO @Bookings (id_BookingSession,id_BookingSession_Slot,TimeFrom,TimeUntil) 
VALUES
(1,90,'20260507 10:05','20260507 10:45')  -- 40 Min booking
,(1,91,'20260507 11:05','20260507 11:30') -- 25 Min booking
,(1,93,'20260507 11:30','20260507 12:30') -- 60 Min booking (So no gap before last one);

SELECT * FROM @BookingSession;
SELECT * FROM @bookings;

在这种场景下,我希望得到如下输出:

Result
09:00 - 10:05 Available
10:45 - 11:05 Available
12:30 - 17:00 Available

解决方案

我只需要把行合并在一起并使用LEAD() 来找出下一行的开始时间。

(一个间隙从当前EndTime开始,到下一行的StartTime结束。)

然后还要把预订会话的那一行也各包含两次,一次作为第一个间隙的起始,一次作为最后一个间隙的结束。

最后排除所有Start不小于End的结果。

注:此处假设预订不能重叠。

WITH
  combined AS
(
  SELECT
    id_BookingSession
   ,TimeFrom
   ,TimeUntil
  FROM
    Bookings

  UNION ALL

  SELECT
    id_BookingSession
   ,NULL
   ,SessionStart
  FROM
    BookingSession

  UNION ALL

  SELECT
    id_BookingSession
   ,SessionEnd
   ,NULL
  FROM
    BookingSession
),
  gaps AS
(
  SELECT
    id_BookingSession,
    TimeUntil AS TimeFrom,
    LEAD(TimeFrom) OVER (
      PARTITION BY id_BookingSession
          ORDER BY TimeFrom
      )
        AS TimeUntil
  FROM
    combined
)
SELECT
  *
FROM
  gaps
WHERE
  TimeFrom < TimeUntil
ORDER BY
  1,2,3

https://dbfiddle.uk/tXLItvWI

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章