我有一個描述軟件版本安裝了哪些會在不同的機器上的表: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