Postgres日期重叠约束

yok*_*oko 19 sql postgresql

我有这样一张桌子:

date_start    date_end     account_id    product_id
2001-01-01    2001-01-31   1             1
2001-02-01    2001-02-20   1             1
2001-04-01    2001-05-20   1             1
Run Code Online (Sandbox Code Playgroud)

我想禁止给定的重叠间隔 (account_id, product_id)

编辑:我找到了一些东西:

CREATE TABLE test (                                                                                                
    from_ts TIMESTAMPTZ,
    to_ts TIMESTAMPTZ,
    account_id INTEGER,
    product_id INTEGER,
    CHECK ( from_ts < to_ts ),
    CONSTRAINT overlapping_times EXCLUDE USING GIST (
        account_id WITH =,
        product_id WITH =,
        box(
            point( extract(epoch FROM from_ts at time zone 'UTC'), extract(epoch FROM from_ts at time zone 'UTC') ),
            point( extract(epoch FROM to_ts at time zone 'UTC') , extract(epoch FROM to_ts at time zone 'UTC') )
        ) WITH &&
    )
);
Run Code Online (Sandbox Code Playgroud)

如果您想了解更多有关此内容的信息,请访问http://www.depesz.com/2010/01/03/waiting-for-8-5-exclusion-constraints/

我唯一的问题是它不能使用空值作为结束时间戳,我想用无限值替换它但不起作用.

yok*_*oko 26

好的,我最终做到了这一点:

CREATE TABLE test (
    from_ts TIMESTAMPTZ,
    to_ts TIMESTAMPTZ,
    account_id INTEGER DEFAULT 1,
    product_id INTEGER DEFAULT 1,
    CHECK ( from_ts < to_ts ),
    CONSTRAINT overlapping_times EXCLUDE USING GIST (
        account_id WITH =,
        product_id WITH =,
        period(from_ts, CASE WHEN to_ts IS NULL THEN 'infinity' ELSE to_ts END) WITH &&
    )
);
Run Code Online (Sandbox Code Playgroud)

与无限,交易证明完美配合.

我只需安装时间扩展,这将在postgres 9.2中生成,btree_gist可作为9.1中的扩展. CREATE EXTENSION btree_gist;

nb:如果你没有空时间戳,就不需要使用时间扩展,你可以使用我的问题中指定的box方法.

  • 更好的是,9.2的范围类型将NULL视为+/-无穷大.因此,您可以直接使用`to_ts`而不是`CASE`甚至`COALESCE`. (3认同)
  • 在9.2版本中,添加了范围类型,包括日期范围,简化了您的constaint和整个问题域.http://www.postgresql.org/docs/devel/static/rangetypes.html (2认同)
  • 您可以使用`COALESCE(to_ts,'infinity':: timestamptz)`而不是`CASE WHEN`来减少`UPPERCASE WORD SALAD` (2认同)
  • 我没有尝试过,但看起来这样可以在9.2中工作,而不必通过用`tstzrange()`替换`period()`函数来安装时延扩展. (2认同)

min*_*alf 7

在最新的 postgres 版本中(我在 9.6 中对其进行了测试,但我认为它可以在 >=9.2 中工作)您可以使用tstzrange()其他一些评论中提到的内置函数。默认情况下,空值将被视为正无穷大或负无穷大,然后不再明确需要 CHECK 约束(如果您认为检查只是<=一个范围可以以相同日期开始和结束)。只btree_gist需要扩展名:

CREATE EXTENSION btree_gist;

CREATE TABLE test (
    from_ts TIMESTAMPTZ,
    to_ts TIMESTAMPTZ,
    account_id INTEGER DEFAULT 1,
    product_id INTEGER DEFAULT 1,
    CONSTRAINT overlapping_times EXCLUDE USING GIST (
        account_id WITH =,
        product_id WITH =,
        TSTZRANGE(from_ts, to_ts) WITH &&
    )
);
Run Code Online (Sandbox Code Playgroud)

  • 嗨@mineralf,实际上我再次检查了,我的初始表有“DATE”时间(因此没有时区信息)。在这种情况下,尝试使用“TSTZRANGE”函数添加约束会失败并出现以下错误:“索引表达式中的函数必须标记为 IMMUTABLE”。TL;DR: - 如果您的列类型是“DATE”/“TIMESTAMP”,请使用“TSRANGE”作为约束中的函数 - 如果您的列类型是“TIMESTAMPTZ”,则使用“TSTZRANGE”作为约束中的函数 (2认同)