【问题标题】:'one-to-many' relation integrity issue for time ranges时间范围的“一对多”关系完整性问题
【发布时间】:2013-12-06 09:32:04
【问题描述】:

假设我有这样的表:

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

【问题讨论】:

    标签: postgresql database-design constraints date-range partial-index


    【解决方案1】:

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

    “时间戳”似乎是指某个时间段

    range type 上的 exclusion constraint,结合 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 9.2 或更高版本。

    相关:

    The manual has a matching code example!

    【讨论】:

    • 很好的答案 - 似乎正是我所需要的。非常感谢 。我目前使用的是 Postres 9.1,但很快就会切换到 9.3。
    【解决方案2】:

    由于我切换到 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 &&
      )
    );
    

    【讨论】:

      猜你喜欢
      • 2012-07-07
      • 1970-01-01
      • 2021-09-06
      • 1970-01-01
      • 2016-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多