2

假设我有这样的表:

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. 我知道我可以写一个触发器(这似乎是我现在唯一的选择),但也许有人有一个更简单的想法?我正在考虑使用某种部分索引,但我不确定如何编写条件......

4

2 回答 2

4

我需要确定,对于某个时间戳,表中只有foo一行foo.barid

而“时间戳”似乎是指一段时间

范围类型排除约束,结合上的相等(利用附加模块)将是完美的解决方案。baridbtree_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 9.2或更高版本。

有关的:

该手册有一个匹配的代码示例!

于 2013-12-11T08:11:35.990 回答
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 &&
  )
);
于 2013-12-13T10:15:13.320 回答