如果postgres中的数据库中已存在,则查询检查开始日期和结束日期之间的日期范围

时间:2017-04-28 06:12:52

标签: sql postgresql date

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

例如,我需要从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'

4 个答案:

答案 0 :(得分:2)

您的查询未给出预期结果,因为您只测试3种(或4种)重叠/交叉方式中的1种:

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

要测试每种可能的重叠方式,您可以使用daterange的重叠运算符:&&

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的重叠运算符(&&)的工作方式与@a_horse_with_no_name提到的overlaps运算符的工作方式相同。不同的是:

  • 它支持排除约束
  • 它支持使用索引(如果添加了排除约束,则会自动为您创建索引)
  • 您可以微调如何包含范围的边界。默认值为[),表示:下限是包含的,但上限是独占的。使用overlaps,这是支持的包含。
  • overlaps适用于timestamp with time zone个值。您的date值会被投放到该值,使用午夜作为时间和时间。当前的TimeZone设置。这可能是也可能不是你想要的。

http://rextester.com/NNWD52481

答案 1 :(得分:1)

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

TO_DATE&gt; = [您的开始日期] AND TO_DATE&lt; = [您的结束日期]
OR
FROM_DATE&lt; = [您的结束日期] AND FROM_DATE&gt; = [您的开始日期]

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';

答案 2 :(得分:0)

使用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也是假的。

答案 3 :(得分:0)

我也有同样的问题。以下查询将检查db中是否存在给定的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)))