7

さまざまな時点でマシンにインストールされたソフトウェアバージョンを説明する表があります。

machine_id::integer, version::text, datefrom::timestamp, dateto::timestamp

日付範囲が重複しないように制約を設定したいと思います。つまり、マシンに複数のソフトウェアバージョンを同時にインストールすることはできません。

これはSQLでどのように達成できますか?PostgreSQLv8.4を使用しています。

4

6 に答える 6

14

PostgreSQL 8.4では、これはトリガーでのみ解決できます。トリガーは、挿入/更新時に競合する行が存在しないことを確認する必要があります。トランザクションの直列化可能性は述語ロックを実装していないため、必要なロックを自分で行う必要があります。これを行うにSELECT FOR UPDATEは、machinesテーブルの行を使用して、競合する可能性のあるデータを他のトランザクションが同時に挿入できないようにします。

PostgreSQL 9.0では、除外制約と呼ばれる、これに対するより良い解決策があります(CREATE TABLEである程度文書化されています)。これにより、日付範囲が重複してはならないという制約を指定できます。その機能の作者であるJeffDavisは、これについて2つの部分からなる記事を書いています:パート1パート2Depeszには、この機能を説明するコード例もいくつかあります。

于 2010-03-19T12:17:29.750 に答える
10

それまでの間(マニュアルを正しく読んだ場合はバージョン9.2以降)、postgreSQLは範囲タイプのサポートを追加しました。

これらの範囲タイプでは、問題は突然非常に単純になります(マニュアルからコピーされた例):

CREATE TABLE reservation (
    during tsrange,
    EXCLUDE USING gist (during WITH &&)
);

以上です。テスト(マニュアルからもコピー):

INSERT INTO reservation VALUES
    ('[2010-01-01 11:30, 2010-01-01 15:00)');

INSERT 0 1

INSERT INTO reservation VALUES
    ('[2010-01-01 14:45, 2010-01-01 15:45)');

エラー:競合するキー値が除外制約 "reservation_during_excl"に違反しています詳細:キー(during)=(["2010-01-01 14:45:00"、 "2010-01-01 15:45:00"))が既存のものと競合していますkey(during)=(["2010-01-01 11:30:00"、 "2010-01-01 15:00:00"))。

于 2015-03-12T17:36:41.053 に答える
1
CREATE EXTENSION IF NOT EXISTS btree_gist; 

CREATE TABLE machines(
    machine_id integer, 
    version text, 
    during tsrange,
    EXCLUDE USING gist ( machine_id with =, during with &&)
);

テーブル内の同じIDを持つマシンは重複しません。

于 2021-05-06T23:09:05.723 に答える
0

タイトルに記載されているように、本当にCHECKコストレインが必要ですか?CHECK制約は一度に1行しか機能できないため、これは不可能です。ただし、トリガーを使用してこれを行う方法があるかもしれません...

于 2010-03-19T11:35:09.577 に答える
0
-- Implementation of a CONSTRAINT on non-overlapping datetime ranges
-- , using the Postgres rulesystem.
-- This mechanism should work for 8.4, without needing triggers.(tested on 9.0)
-- We need a shadow-table for the rangesonly 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;
于 2011-10-22T14:17:21.937 に答える
0

何らかの理由でテーブルスキーマを変更できず、2つの時間行を保持する必要がある場合は、次のように制約内で範囲を構築できます。

CREATE TABLE reservations (
    datefrom timestamp,
    dateto timestamp,
    EXCLUDE USING gist (tsrange(datefrom, dateto) WITH &&)
);

この場合、タイムスタンプタイプを処理するためにtsrangeを使用しましたが、他にも使用できるものがあります。https://www.postgresql.org/docs/current/rangetypes.htmlのドキュメントを確認してください

于 2020-06-22T00:31:53.280 に答える