【问题标题】:Checking if booking is possible in SQL (Postgresql)检查是否可以在 SQL (Postgresql) 中预订
【发布时间】:2015-09-26 10:40:46
【问题描述】:

我有这样看起来记录的餐桌预订:

  id  |        from         |         to
------+---------------------+---------------------
  101 | 2015-09-24 08:00:00 | 2015-09-24 09:30:00
 2261 | 2015-09-24 09:00:00 | 2015-09-24 10:00:00
 4061 | 2015-09-24 10:00:00 | 2015-09-24 10:30:00
  204 | 2015-09-24 12:00:00 | 2015-09-24 13:30:00
 2400 | 2015-09-24 13:30:00 | 2015-09-24 14:00:00
 4224 | 2015-09-24 14:00:00 | 2015-09-24 14:30:00
  309 | 2015-09-24 16:00:00 | 2015-09-24 17:30:00
 2541 | 2015-09-24 17:00:00 | 2015-09-24 18:00:00

我正在寻找最佳查询来找到问题的答案:

Is this possible to find a timeslot with duration x (ie. 30 minutes) in above records?

我有使用 postgres 数组或时间范围的想法,但仍在寻找更好的想法......

编辑:我将提供“假”预订作为界限,但如果您有更好的方法,请写信:)

【问题讨论】:

  • 这个问题似乎不完整。如果您没有预订,那么您不能对时间段进行细化。换句话说,您需要提供开始和结束信息。
  • @GordonLinoff :你是对的。我目前假设 [ -infinity,+infinity ]
  • 我编辑了问题,谢谢通知!

标签: sql database postgresql gaps-in-data


【解决方案1】:

类似这样的:

select t1.*
from tablename t1
where (select min("from") from tablename t2
       where t2."from" > t1."from") >= t1."to" + interval '30' minute

即如果与下一行的间隔 >= 30 分钟,则返回一行。

注意fromto 是 ANSI SQL 中的保留字,因此它们被分隔为 "from""to"

【讨论】:

    【解决方案2】:

    这是一个使用分析函数的解决方案,它提供了所有窗口都没有预订:

    SELECT null as ts_from, min(ts_from) as ts_to
      FROM bookings
     UNION ALL
    SELECT ts as ts_from, next_ts as ts_to
      FROM (SELECT ts, lead(ts, 1) over (order by ts) as next_ts, sum(bk) over (order by ts) as bksum
              FROM (SELECT ts_from as ts, 1 as bk
                      FROM bookings
                     UNION ALL
                    SELECT ts_to as ts, -1 as bk
                      FROM bookings) as t) as tt
     WHERE bksum = 0
     ORDER BY 1 NULLS FIRST;
    

    SQL 小提琴here.

    【讨论】:

      【解决方案3】:

      你可以使用lag()函数:

      select *, book_start- previous_book_end timeslot
      from (
          select id, "from" book_start, "to" book_end, 
          lag("to") over (order by "to") previous_book_end
          from test
          ) sub
      order by book_end
      
        id  |     book_start      |      book_end       |  previous_book_end  | timeslot
      ------+---------------------+---------------------+---------------------+-----------
        101 | 2015-09-24 08:00:00 | 2015-09-24 09:30:00 |                     |
       2261 | 2015-09-24 09:00:00 | 2015-09-24 10:00:00 | 2015-09-24 09:30:00 | -00:30:00
       4061 | 2015-09-24 10:00:00 | 2015-09-24 10:30:00 | 2015-09-24 10:00:00 | 00:00:00
        204 | 2015-09-24 12:00:00 | 2015-09-24 13:30:00 | 2015-09-24 10:30:00 | 01:30:00
       2400 | 2015-09-24 13:30:00 | 2015-09-24 14:00:00 | 2015-09-24 13:30:00 | 00:00:00
       4224 | 2015-09-24 14:00:00 | 2015-09-24 14:30:00 | 2015-09-24 14:00:00 | 00:00:00
        309 | 2015-09-24 16:00:00 | 2015-09-24 17:30:00 | 2015-09-24 14:30:00 | 01:30:00
       2541 | 2015-09-24 17:00:00 | 2015-09-24 18:00:00 | 2015-09-24 17:30:00 | -00:30:00
      (8 rows)    
      

      选择带有timeslots >= '30m'::interval的行:

      select *, book_start- previous_book_end timeslot
      from (
          select id, "from" book_start, "to" book_end, 
          lag("to") over (order by "to") previous_book_end
          from test
          ) sub
      where book_start- previous_book_end >= '30m'::interval
      order by book_end
      
       id  |     book_start      |      book_end       |  previous_book_end  | timeslot
      -----+---------------------+---------------------+---------------------+----------
       204 | 2015-09-24 12:00:00 | 2015-09-24 13:30:00 | 2015-09-24 10:30:00 | 01:30:00
       309 | 2015-09-24 16:00:00 | 2015-09-24 17:30:00 | 2015-09-24 14:30:00 | 01:30:00
      (2 rows)
      

      【讨论】:

      • 您不能只查看以前的预订。想象一下第一次预订涵盖了整个时间跨度。您将报告错误的插槽 (example)。
      • 当然,我可以想象一切。也许更好的主意是让 OP 评估这种假设情况是否会发生?
      • 好吧,我弄错了,我们可以假设预订不能重叠(例如它确实重叠,但表中有一些外键)。
      • 这与我对预订本质的理解是一致的。我的回答中的查询有助于避免这种情况。好吧,@Kombajnzbożowy,投反对票有点为时过早,不是吗?
      • 是的,在不重叠预订的约束下,您的解决方案很好。一开始我只是假设了最坏的情况。
      【解决方案4】:

      非标准自加入:

      SELECT
          ll.ts_to AS ts_from
          , hh.ts_from AS ts_to
      FROM bookings ll
      JOIN bookings hh
          -- enough space 
          ON hh.ts_from >= ll.ts_to + '30 min'::interval
          -- and nothing in between
          AND NOT EXISTS (
              SELECT * FROM bookings nx
              WHERE nx.ts_from >= ll.ts_to
              AND nx.ts_to <= hh.ts_from
              )
      UNION ALL   -- before the first
      SELECT '-infinity'::timestamp AS ts_from
             , MIN(ts_from) AS ts_to
          FROM bookings
      UNION ALL   -- after the last
      SELECT MAX(ts_to) AS ts_from
             , 'infinity'::timestamp AS ts_to
          FROM bookings
      ORDER BY 1,2
          ;
      

      【讨论】:

        【解决方案5】:

        生成您的插槽,然后加入它们。 http://sqlfiddle.com/#!15/12bfa

        create table t (id integer, "from" timestamp, "to" timestamp);
        
        insert into t values 
        (101 , '2015-09-24 08:00:00' , '2015-09-24 09:30:00' ),
        (2261 , '2015-09-24 09:00:00' , '2015-09-24 10:00:00' ),
        (4061 , '2015-09-24 10:00:00' , '2015-09-24 10:30:00' ),
        ( 204 , '2015-09-24 12:00:00' , '2015-09-24 13:30:00' ),
        (2400 , '2015-09-24 13:30:00' , '2015-09-24 14:00:00' ),
        (4224 , '2015-09-24 14:00:00' , '2015-09-24 14:30:00' ),
        ( 309 , '2015-09-24 16:00:00' , '2015-09-24 17:30:00' ),
        (2541 , '2015-09-24 17:00:00' , '2015-09-24 18:00:00' );
        
        SELECT time_slots.t,
               time_slots.t + interval '30 minutes'
        FROM generate_series(date'2015-09-24',date'2015-09-25' - interval '30 minutes' ,interval '30 minutes') AS time_slots(t)
        LEFT JOIN t ON (time_slots.t BETWEEN t."from" AND t."to")
        WHERE t.id IS NULL;
        
        
        SELECT time_slots.t,
               time_slots.t + interval '30 minutes'
        FROM generate_series(date'2015-09-24',date'2015-09-25' - interval '30 minutes',interval '30 minutes') AS time_slots(t)
        LEFT JOIN t ON ((time_slots.t,
                         time_slots.t + interval '30 minutes') OVERLAPS (t."from",
                                                                         t."to"))
        WHERE t.id IS NULL;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-13
          • 1970-01-01
          • 2019-09-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多