【问题标题】:Table level constraint to prevent overlapping date ranges防止日期范围重叠的表级约束
【发布时间】:2016-02-20 08:55:39
【问题描述】:

下面是我需要设置约束的架构,以便可以为房间号输入第二个新条目,甚至在相同房间号的现有 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 中不起作用。

【问题讨论】:

  • 您在INSERT 语句的第二个元素末尾缺少)
  • 抱歉,打错字了。
  • 据我所见,只要他们的抵达日期不同,就可以插入具有相同房间号的第二个条目可以插入。你的实际问题是什么?您能否显示预期的输出和错误或您收到的输出?
  • 请同时修正列的命名。我怀疑roomNumroomNo 有什么不同。 arrDtarrDate 也一样

标签: sql postgresql constraints


【解决方案1】:

这可以在日期范围内使用exclusion constraint 来完成:

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

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

【讨论】:

  • SQL Fiddle 没有这个扩展。
【解决方案2】:

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

创建一个函数,根据您的条件检查房间是否可用,并返回一个可用于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'));

第一个值被插入,而当发出第二个插入语句时,你得到一个检查约束违规

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

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

【讨论】:

  • $$ 是什么意思?为什么使用 GO 终结器?
  • @Shashikiran:$$ 被称为美元报价:postgresql.org/docs/current/static/…
  • @Shashikiran 我在 SQL Fiddle 中使用 GO 作为终止符,以便能够在函数中使用 ; 而不会出错。通常这是那里的终止符,并且会产生错误,例如假设 END IF; 是无效语句。
  • 它不适用于多个插入语句 INSERT INTO Accomodation(roomNo, arrDt, depDt) VALUES (123, to_date('20160202', 'YYYYMMDD'), to_date('20160206','YYYYMMDD' )), (123, to_date('20160205', 'YYYYMMDD'), to_date('20160208','YYYYMMDD')) 去
  • @Shashikiran 我已经提到您需要在我的答案中的单独语句中插入两行。为什么你的应用程序会在一个语句中构建多个插入?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-01
  • 2021-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多