Postgres date overlapping constraint

前端 未结 4 1142
鱼传尺愫
鱼传尺愫 2020-12-14 08:22

I have a table like this:

date_start    date_end     account_id    product_id
2001-01-01    2001-01-31   1             1
2001-02-01    2001-02-20   1                 


        
4条回答
  •  无人及你
    2020-12-14 08:32

    This is a difficult problem because constraints can only reference the "current row", and may not contain subqueries. (otherwise the trivial solution would be to add some NOT EXISTS() subquery in the check)

    A check constraint specified as a column constraint should reference that column's value only, while an expression appearing in a table constraint can reference multiple columns.

    Currently, CHECK expressions cannot contain subqueries nor refer to variables other than columns of the current row.

    Popular work-arounds are: use a trigger function which does the dirty work (or use the rule system, which is deprecated by most people)

    Because most people favor triggers, I'll repost a rule-system hack here... (it does not have the extra "id" key element, but that's a minor detail)

    -- Implementation of A CONSTRAINT on non-overlapping datetime ranges
    -- , using the Postgres rulesystem.
    -- We need a shadow-table for the ranges only 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;
    
                                                                                                                          
    

提交回复
热议问题