假设我有这样的表:
CREATE TABLE foo (
id SERIAL PRIMARY KEY
, barid integer NOT NULL REFERENCES bar(id)
, bazid integer NOT NULL REFERENCES baz(id)
, startdate timestamp(0) NOT NULL
, enddate timestamp(0) NOT NULL
);
该表的目的是在表bar和baz之间提供伪“一对多”关系,但关系可以随时间变化:
SELECT * FROM bar
JOIN foo on TRUE
AND foo.barid = bar.id
AND now() BETWEEN foo.startdate AND foo.enddate
JOIN baz on baz.id = foo.bazid
我们可以想象,对于bar
表中的某一行,我们希望在baz
表中找到相应的行,但相应的行在不同的时间段内可能会有所不同 - 所以它应该返回现在不同的行,上个月不同等。
现在我的问题是:在此表中验证数据完整性的最佳方法是什么?具体来说,我需要确定,对于某个时间戳,foo
表foo.barid
中只有一行。我知道我可以写一个触发器(这似乎是我现在唯一的选择),但也许有人有一个更简单的想法?我在考虑使用某种部分索引,但我不确定如何编写条件...
答案 0 :(得分:1)
我需要确定,对于某个时间戳,表
foo
中只有一行foo.barid
通过 timestamp ,您似乎意味着某个时间段。
exclusion constraint上的range type,加上barid
上的相等(使用附加模块btree_gist
)将是完美的解决方案。
CREATE EXTENSION btree_gist; -- needed once per database
CREATE TABLE foo (
fooid serial PRIMARY KEY
, barid integer NOT NULL REFERENCES bar(barid)
, bazid integer NOT NULL REFERENCES baz(bazid)
, time_range tsrange NOT NULL -- replaces startdate & enddate
EXCLUDE USING gist (barid WITH =, time_range WITH &&)
);
您忽略了提供您的Postgres版本。这需要Postgres 9.2 或更高版本。
考虑这个相关的答案:
Preventing adjacent/overlapping entries with EXCLUDE in PostgreSQL
答案 1 :(得分:0)
当我切换到postgres 9.3被推迟时,我最终得到了你提到的帖子中的内容:
CREATE TABLE foo (
id SERIAL PRIMARY KEY
, barid integer NOT NULL REFERENCES bar(id)
, bazid integer NOT NULL REFERENCES baz(id)
, startdate timestamp(0) NOT NULL
, enddate timestamp(0) NOT NULL
EXCLUDE USING gist (
box(
point(
-- this is kind of a dirty hack: as extracting epoch from +/- infinity
-- gives 0, I need to distinguish one from another
date_part('epoch'::text, least( startdate , '2222-01-01') )
, barid
)
, point(
-- same thing here
date_part('epoch'::text, least( enddate , '2222-01-01') )
, barid
)
) WITH &&
)
);