【问题标题】:Query to check date range between start and end dates if already exists in database in postgres如果 postgres 的数据库中已经存在,则查询以检查开始日期和结束日期之间的日期范围
【发布时间】:2017-04-28 06:12:51
【问题描述】:

我必须在两个日期范围之间分配机票预订,即从可用的开始日期和结束日期(之前未分配)。 为此,如果在这些日期之间已经发生了任何分配,我需要检查数据库

例如,我需要分配从 2017/04/20 到 2017/04/25 的机票预订。在插入此记录之前,我需要检查在开始日期 2017/04/20 之间是否有任何预订和结束日期 2017/04/25

我已尝试使用以下查询。但它没有给出正确的结果

select "id" 
from "table" 
where "from_date" >= '2017-04-20' 
  and "to_date" > '2017-04-20' 
  and "from_date" < '2017-04-25' 
  and "to_date" <= '2017-04-25'

【问题讨论】:

    标签: sql postgresql date


    【解决方案1】:

    您的查询没有给出预期的结果,因为您只测试了 3 种(或 4 种)可能的重叠/相交方式中的一种:

    existing date periods:  +--------------+      +-----+       +----+   +-----+
                                contains        contained by        touches
    testing periods:            +------+       +------------+     +---------+
    

    要测试所有可能的重叠方式,您可以使用daterange 的重叠运算符:&amp;&amp;

    select count(*)
    from   booking
    where  daterange(from_date, to_date, '[]') && daterange('2017-04-20', '2017-04-25', '[]');
    

    请注意:仅使用此类手动检查不会阻止以高并发插入重叠的日期范围。在此测试之后和实际插入另一个并发语句之前,有一个(小)窗口来插入冲突的行。为了避免这种情况,您可以使用exclusion constraints:

    alter table booking
      add constraint exclude_overlapping_bookings
      exclude using gist (daterange(from_date, to_date, '[]') with &&);
    

    注意daterange 的重叠运算符 (&amp;&amp;) 的工作方式与 @a_horse_with_no_name 提到的 overlaps 运算符相同。除了:

    • 它支持排除约束
    • 它支持使用索引(如果您添加了排除约束,则会自动为您创建索引)
    • 您可以微调如何包含范围的边界。默认为[),意思是:下界包含,上界不包含。对于 overlaps,这是唯一支持的包含。
    • overlapstimestamp with time zone 值一起使用。您的 date 值将被转换为该值,使用午夜作为时间和当前的 TimeZone 设置。这可能是您想要的,也可能不是。

    http://rextester.com/NNWD52481

    【讨论】:

    【解决方案2】:

    只是为了检查另一种方式,另一个不使用重叠运算符但仅使用 AND et OR 的查询: (使用 a_horse 发布的数据...)。这是逻辑:

    TO_DATE>= [您的开始日期] AND TO_DATE 或
    FROM_DATE = [您的开始日期]

    select *
    from bookings
    where to_date>=date '2017-04-18' AND to_date<=date '2017-04-19'
    OR from_date<= date '2017-04-19' AND from_date>=date '2017-04-18';
    
    select *
    from bookings
    where to_date>=date '2017-04-20' AND to_date<=date '2017-04-25'
    OR from_date<= date '2017-04-25' AND from_date>=date '2017-04-20';
    

    【讨论】:

      【解决方案3】:

      使用overlaps 运算符:

      select count(*) = 0 as allow_booking
      from "table"
      where (from_date, to_date) overlaps (date '2017-04-20', date '2017-04-25');
      

      这也将处理未完全在测试范围内的预订。

      如果允许预订,查询将返回true,否则返回false

      例子:

      create table bookings 
      (
        id serial,
        from_date date, 
        to_date date
      );
      
      insert into bookings 
        (from_date, to_date) 
      values 
        (date '2017-04-20', date '2017-04-20'),
        (date '2017-04-20', date '2017-04-24'),
        (date '2017-04-26', date '2017-04-29');
      

      那么下面

      select *
      from bookings
      where (from_date, to_date) overlaps (date '2017-04-18', date '2017-04-19');
      

      不会返回任何内容,因此,count(*) = 0 返回true

      以下查询:

      select *
      from bookings
      where (from_date, to_date) overlaps (date '2017-04-20', date '2017-04-25');
      

      返回:

      id | from_date  | to_date   
      ---+------------+-----------
       1 | 2017-04-20 | 2017-04-20
       2 | 2017-04-20 | 2017-04-24
      

      所以count(*) = 0 返回false

      还有查询:

      select *
      from bookings
      where (from_date, to_date) overlaps (date '2017-04-27', date '2017-04-28');
      

      将返回:

      id | from_date  | to_date   
      ---+------------+-----------
       3 | 2017-04-26 | 2017-04-29
      

      count(*) = 0 也是错误的。

      【讨论】:

      • 但我必须允许不在此范围内的预订。在这种情况下,查询不起作用
      • @user2403954:查询将返回那些属于该范围的 id。因此,如果查询没有返回任何内容,您可以允许预订,否则您必须拒绝预订。您也可以使用count(*) 来检查(因为您并不真正关心哪些 预订存在,只如果 存在一个)
      • 在我的餐桌上预订仅存在于 2017 年 4 月 20 日。现在我尝试使用日期范围(from_date)2017-04-18 和 to_date 2017-04-19。在这种情况下,您所说的预期计数为 0,即,它应该允许在这些日期进行预订分配。但我算作 2。
      • @user2403954:如果您在2017-04-20 上进行预订,那么2017-04-18, 2017-04-19 的重叠肯定不会返回2:请参见此处的示例:@987654321 @
      • 工作.. 谢谢
      【解决方案4】:

      我也遇到了同样的问题。下面的查询将检查给定的 start_date 或 end_date 是否存在于数据库中。

      $start_date = '2010-01-01';
      $end_date = '2017-01-01';
      
      SELECT * FROM `ticket_history` WHERE 
      ($start_date <= start_date AND $start_date <= end_date AND 
      (($end_date >= start_date AND $end_date >= end_date) OR ($end_date >= 
      start_date AND $end_date <= end_date))) OR
      ($start_date >= start_date AND $start_date <= end_date AND 
      (($end_date >= start_date AND $end_date >= end_date) OR ($end_date >= 
      start_date AND $end_date <= end_date)))
      

      【讨论】:

        猜你喜欢
        • 2022-01-13
        • 2019-12-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-31
        相关资源
        最近更新 更多