0

同じ object_id と user_id を持つアクティブなレコードが常に 1 つだけであることを確認する必要がある状況があります。代表的な表は次のとおりです。

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true,
    created_at timestamptz default now()
);

一度に 1 つのアクティブ レコードのみということは、次のような一連の挿入を行うことができるということです。

insert into actions (object_id, user_id, active) values (1, 1, true);
insert into actions (object_id, user_id, active) values (1, 1, false);

しかし、その後の

insert into actions (object_id, user_id, active) values (1, 1, true);

この時点で、object_id = 1 および user_id = 1 のアクティブなタプルが既に 1 つ存在するため、失敗するはずです。

PostgreSQL 8.4 を使用しています。

面白そうなこの投稿を見ましたが、その Oracle 固有のものです。

この投稿も見ましたが、トランザクションの分離レベルに関してもっと注意が必要です。リードコミットモードではそのままでは機能しないと思います。

私の質問は、この種の制約が不明な場合に利用できる他のオプションは何ですか?

編集: 最初のセットの 3 番目の挿入を削除しました。私はそれが例を混乱させたと思います。コンテキストに役立つように、created_at タイム スタンプも追加しました。繰り返しますが、(object_id、user_id、false) タプルは複数存在できますが、(object_id、user_id、true) タプルは 1 つだけです。

更新: Craig の回答を受け入れましたが、似たようなものに出くわす可能性のある他の人のために、別の (最適ではありませんが) 解決策があります。

CREATE TABLE action_consistency (
    object_id integer,
    user_id integer,
    count integer default 0,
    primary key (object_id, user_id),
    check (count >= 0 AND count <= 1)
);


CREATE OR REPLACE FUNCTION keep_action_consistency()
  RETURNS TRIGGER AS
$BODY$
    BEGIN

    IF NEW.active THEN
        UPDATE action_consistency
        SET count = count + 1
        WHERE object_id = NEW.object_id AND
        user_id   = NEW.user_id;

        INSERT INTO action_consistency (object_id, user_id, count)
        SELECT NEW.object_id, NEW.user_id, 1
        WHERE NOT EXISTS (SELECT 1 
                          FROM action_consistency
                          WHERE object_id = NEW.object_id AND
                                user_id   = NEW.user_id);
    ELSE
        -- assuming insert will be active for simplicity
        UPDATE action_consistency
        SET count = count - 1
        WHERE object_id = NEW.object_id AND
        user_id   = NEW.user_id;
    END IF;

    RETURN NEW;

    END;
$BODY$
LANGUAGE plpgsql;

CREATE TRIGGER ensure_action_consistency AFTER INSERT OR UPDATE ON actions
    FOR EACH ROW EXECUTE PROCEDURE keep_action_consistency();

追跡テーブルを使用する必要があります。明らかな理由であることを願っていますが、これはまったく望ましくありません。これは、アクションに個別の (object_id、user_id) ごとに追加の行があることを意味します。

@Craig Ringer の回答を受け入れたもう 1 つの理由は、他のテーブルの actions.id への外部キー参照があり、特定のアクション タプルの状態が変化したときに非アクティブになることです。これが、このシナリオで履歴テーブルがあまり理想的でない理由です。コメントと回答ありがとうございます。

4

3 に答える 3

2

一度に 1 つのエントリのみを制限するという指定がある場合は、次activeを試してください。

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true,
    created_at timestamptz default now()
);

CREATE UNIQUE INDEX actions_unique_active_y ON actions(object_id,user_id) WHERE (active = 't');

これは部分的な一意のインデックスであり、PostgreSQL 固有の機能です。部分インデックスを参照してください。isの(object_id,user_id)場所にタプルが1 つしか存在しないように、セットを制約します。activetrue

コメントでさらに説明したように、それはあなたの質問に厳密に答えますが、ワイルドプラッサーの答えはより正しい選択と最良のアプローチを説明していると思います。

于 2012-09-11T08:20:00.663 に答える
2

UNIQUE 制約を使用して、列に一意の値が含まれるようにすることができます... ここでは、 と のセットが一意object_iduser_id作成されています....

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true,
    UNIQUE (object_id , user_id )

);

SQLFIDDLEをチェックしてください

object_id同様に、user_idactiveをセットにしたい場合はUNIQUE、 のリストに列名を追加するだけですUNIQUE

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true,
    UNIQUE (object_id , user_id,active )

);

SQLFIDDLEをチェックしてください

于 2012-09-10T22:38:33.363 に答える
1

オリジナル:

CREATE TABLE actions (
    id SERIAL PRIMARY KEY,
    object_id integer,
    user_id integer,
    active boolean default true
);

私のバージョン:

CREATE TABLE actions (
    object_id integer NOT NULL REFERENCES objects (id),
    user_id integer NOT NULL REFERENCES users(id),
    PRIMARY KEY (user_id, object_id)
);

違いは何ですか:

  • 代理キーを省略しました。それは役に立たず、制約を課さず、誰もそれを参照することはありません
  • (複合)主キーを追加しました。これはたまたま論理キーです
  • 2 つのフィールドを NOT NULL に変更し、それらを外部キーにしました (users または objects テーブルに存在しない行の意味は何でしょうか?
  • ブールフラグを削除しました。存在しない {user_id,object_id} タプルと、存在するが「アクティブ」フラグが false に設定されているタプルのセマンティックの違いは何ですか? 2 つの状態しか必要ないのに、なぜ 3 つの状態を作成するのでしょうか。
于 2012-09-10T22:57:18.353 に答える