我有一个描述软件版本安装了哪些会在不同的机器上的表:SQL CHECK约束,以防止日期重叠
machine_id::integer, version::text, datefrom::timestamp, dateto::timestamp
我想要做的一个约束,以确保没有日期范围重叠即,同时在一台机器上安装多个软件版本是不可能的。
这怎么能在SQL中实现?我正在使用PostgreSQL v8.4。
我有一个描述软件版本安装了哪些会在不同的机器上的表:SQL CHECK约束,以防止日期重叠
machine_id::integer, version::text, datefrom::timestamp, dateto::timestamp
我想要做的一个约束,以确保没有日期范围重叠即,同时在一台机器上安装多个软件版本是不可能的。
这怎么能在SQL中实现?我正在使用PostgreSQL v8.4。
在PostgreSQL 8.4中,这只能用触发器来解决。触发器必须检查插入/更新是否存在冲突行。由于事务可序列化不会实现谓词锁定,因此您必须自己进行必要的锁定。在机器表中这样做,以便其他事务不能同时插入可能冲突的数据。
在PostgreSQL 9.0中,会有更好的解决方案,称为排除约束(在CREATE TABLE下有所记录)。这将允许您指定日期范围不得重叠的约束条件。杰夫戴维斯,该功能的作者有两个部分写在这:part 1,part 2。 Depesz也有一些code examples describing the feature。
你真的想要检查costraint,就像标题中提到的一样吗?这是不可能的,因为CHECK约束一次只能工作一行。可能有办法使用触发器来做到这一点,尽管...
-- Implementation of a CONSTRAINT on non-overlapping datetime ranges
-- , using the Postgres rulesystem.
-- This mechanism should work for 8.4, without needing triggers.(tested on 9.0)
-- We need a shadow-table for the rangesonly to avoid recursion in the rulesystem.
-- This shadow table has a canary variable with a CONSTRAINT (value=0) on it
-- , and on changes to the basetable (that overlap with an existing interval)
-- an attempt is made to modify this variable. (which of course fails)
-- CREATE SCHEMA tmp;
DROP table tmp.dates_shadow CASCADE;
CREATE table tmp.dates_shadow
(time_begin timestamp with time zone
, time_end timestamp with time zone
, overlap_canary INTEGER NOT NULL DEFAULT '0' CHECK (overlap_canary=0)
);
ALTER table tmp.dates_shadow
ADD PRIMARY KEY (time_begin,time_end)
;
DROP table tmp.dates CASCADE;
CREATE table tmp.dates
(time_begin timestamp with time zone
, time_end timestamp with time zone
, payload varchar
);
ALTER table tmp.dates
ADD PRIMARY KEY (time_begin,time_end)
;
CREATE RULE dates_i AS
ON INSERT TO tmp.dates
DO ALSO (
-- verify shadow
UPDATE tmp.dates_shadow ds
SET overlap_canary= 1
WHERE (ds.time_begin, ds.time_end)
OVERLAPS (NEW.time_begin, NEW.time_end)
;
-- insert shadow
INSERT INTO tmp.dates_shadow (time_begin,time_end)
VALUES (NEW.time_begin, NEW.time_end)
;
);
CREATE RULE dates_d AS
ON DELETE TO tmp.dates
DO ALSO (
DELETE FROM tmp.dates_shadow ds
WHERE ds.time_begin = OLD.time_begin
AND ds.time_end = OLD.time_end
;
);
CREATE RULE dates_u AS
ON UPDATE TO tmp.dates
WHERE NEW.time_begin <> OLD.time_begin
AND NEW.time_end <> OLD.time_end
DO ALSO (
-- delete shadow
DELETE FROM tmp.dates_shadow ds
WHERE ds.time_begin = OLD.time_begin
AND ds.time_end = OLD.time_end
;
-- verify shadow
UPDATE tmp.dates_shadow ds
SET overlap_canary= 1
WHERE (ds.time_begin, ds.time_end)
OVERLAPS (NEW.time_begin, NEW.time_end)
;
-- insert shadow
INSERT INTO tmp.dates_shadow (time_begin,time_end)
VALUES (NEW.time_begin, NEW.time_end)
;
);
INSERT INTO tmp.dates(time_begin,time_end) VALUES
('2011-09-01', '2011-09-10')
, ('2011-09-10', '2011-09-20')
, ('2011-09-20', '2011-09-30')
;
SELECT * FROM tmp.dates;
EXPLAIN ANALYZE
INSERT INTO tmp.dates(time_begin,time_end) VALUES ('2011-09-30', '2011-10-04')
;
INSERT INTO tmp.dates(time_begin,time_end) VALUES ('2011-09-02', '2011-09-04')
;
SELECT * FROM tmp.dates;
SELECT * FROM tmp.dates_shadow;
同时(因为9.2如果我读正确的手动版本)PostgreSQL有添加rangetypes支持。
这个问题突然变得非常简单的rangetypes(例如从手动复制):
CREATE TABLE reservation (
during tsrange,
EXCLUDE USING gist (during WITH &&)
);
就是这样。测试(也从手动复制):
INSERT INTO reservation VALUES
('[2010-01-01 11:30, 2010-01-01 15:00)');
INSERT 0 1
INSERT INTO reservation VALUES
('[2010-01-01 14:45, 2010-01-01 15:45)');
ERROR: conflicting key value violates exclusion constraint "reservation_during_excl" DETAIL: Key (during)=(["2010-01-01 14:45:00","2010-01-01 15:45:00")) conflicts with existing key (during)=(["2010-01-01 11:30:00","2010-01-01 15:00:00")).
约束的数据将是足够的任何方式。我只是(错误!)认为它将是一个CHECK ... – Michael 2010-03-19 11:46:41