表级别约束以防止重叠日期范围

时间:2016-02-20 08:55:39

标签: sql postgresql constraints

下面是我需要设置一个约束的模式,以便第二个新条目可以放入房间号,甚至在相同房间号的现有depDt之前。你们中的任何人可以帮助我吗?

CREATE TABLE Accomodation (
  roomNo INTEGER NOT NULL,
  arrDt DATE NOT NULL,
  depDt DATE NOT NULL,
  PRIMARY KEY (roomNo, arrDt), 
  CONSTRAINT date_chk CHECK (arrDt < depDt)
);

INSERT INTO HotelStays(roomNo, arrDt, depDt) VALUES 
  (123, to_date('20160202', 'YYYYMMDD'),to_date('20160206','YYYYMMDD')),
  (123, to_date('20160205', 'YYYYMMDD'), to_date('20160208','YYYYMMDD'));

我试过在CONSTRAINTS的WHERE下给出一个子查询,但它在SQL Fiddle中不起作用。

2 个答案:

答案 0 :(得分:2)

可以使用日期范围内的exclusion constraint来完成此操作:

alter table Accomodation
  add constraint no_overlap 
  exclude using gist (roomno with =, daterange(arrdt, depdt) with &&);

请注意,您需要btree_gist extension来支持GiST索引中的=运算符。

答案 1 :(得分:1)

注意:这并不能解决竞争条件的问题。

创建一个函数,根据您的条件检查房间是否可用,并返回可在CHECK约束中使用的标量布尔值。

在这里,您可以预览它的工作原理(记得取消注释最后一个插入语句):SQL FIDDLE

CREATE FUNCTION is_room_available(int, date)
RETURNS boolean
STABLE
LANGUAGE plpgsql
AS 
$$
BEGIN
  IF EXISTS ( SELECT 1 FROM Accomodation WHERE roomNo = $1 AND $2 BETWEEN arrDt AND depDt ) THEN
    RETURN false;
  END IF;

  RETURN true;
END;
$$;

使用新约束创建表

CREATE TABLE Accomodation (
  roomNo INTEGER NOT NULL,
  arrDt DATE NOT NULL,
  depDt DATE NOT NULL,
  PRIMARY KEY (roomNo, arrDt), 
  CONSTRAINT date_chk CHECK (arrDt<depDt),
  CONSTRAINT room_avail CHECK (is_room_available(roomNo, arrDt)) -- added
  );

尝试在单独的语句中插入两行

INSERT INTO Accomodation(roomNo, arrDt, depDt)
VALUES 
(123, to_date('20160202', 'YYYYMMDD'), to_date('20160206','YYYYMMDD'));

INSERT INTO Accomodation(roomNo, arrDt, depDt)
VALUES 
(123, to_date('20160205', 'YYYYMMDD'), to_date('20160208','YYYYMMDD'));

插入第一个值,而在发出第二个插入语句时,您会收到检查约束违规

  

错误:关系“accomodation”的新行违反了检查约束“room_avail”详细信息:失败行包含(123,2016-02-05,2016-02-08)。

注意:这也可以使用触发器轻松实现。您只需稍微修改一下该函数并发出CREATE TRIGGER语句。