2011-07-19 2 views
8

トランザクションがコミットする直前にトリガーを実行することが間接的に可能かどうか疑問に思っていましたか?このトリガーでは、必要に応じて一貫性チェックとトランザクションのロールバックを行います。postgres - トランザクションコミット前のトリガー

は例えば、私は3つのテーブルを持っている:

users (id, name) 
groups (id, name) 
user_in_group (user_id, group_id) 

私は、ユーザーが常にグループの一部であることを確認するトリガーを作成したいと思います。孤児のユーザーは許可されません。ユーザーへの挿入が発生するたびに、このトリガーはuser_in_groupへの対応する挿入も発生したことを確認します。そうでない場合、トランザクションはコミットされません。

これは、上記のシナリオでは2つの独立したステートメントが必要なため、単純な行ベースまたは文ベースのトリガーを使用して実行することはできません。

他の方法で、user_in_groupからの削除が発生した場合、行ベースのトリガーで簡単に行うことができます。

答えて

0

からthe docs。 。 。

トリガーは、変更された行ごとに一度、または SQL文ごとに一度のいずれかで、任意のINSERTの前または後のいずれか UPDATEを実行するために定義された、または操作を削除することができます。

+0

私が知っています。このタイプのトリガーを使用して質問に記載されたシナリオを実行することはできません。それが私が間接的な方法を探していた理由です。あなたは一人知っていますか? –

1

ドキュメントでLookindは、そのようなトリガオプション...達成するので、一つの方法はないように思える「全く孤立したユーザー」ルールはusersuser_in_groupテーブルに直接挿入を許可しないようにしないでしょう。代わりに、ビュー(これらのテーブルを組み合わせたもの、つまりuser_id, user_name, group_id)を右テーブルにデータを挿入するupdate ruleで作成します。

または、必要なすべてのデータをinpudとして受け取り、グループに所属するユーザーを許可しないストアドプロシージャを使用して、新しいユーザーを挿入することができます。

ところで、なぜユーザーとグループの関係に別のテーブルがありますか?なぜgroup_idフィールドにテーブルにFK/NOT NULLの制約を追加しないでください?

+0

更新ルールを表示するとクールなアイデアです、ありがとう!あなたの他の提案について、どのように機能するのでしょうか?プロシージャの外部でクエリを実行することとの違いは何ですか? –

+0

SPの考え方は、両方のインサートを1つのコマンドに結合することです - どの時点でも失敗した場合(つまり 'users'テーブルには既に挿入されていますが' user_group'には挿入されていません)、すべてのアクションがロールバックされ、孤児の記録で – ain

+0

ああ。しかし、SPだけを使用することで、ユーザーがテーブルに直接アクセスするのを防ぐことはできません。 –

1

実際にデータベースに制約を設定する正しい方法はありませんか?それは厳密に何のためのものかと思われます。外部キー制約とnullでないものを追加すると、ビジネスに参加する必要があるように見えます。

禁断の
drop table foousers cascade; 
drop table foogroups cascade; 
drop table foousergrps cascade; 
create table foousers (id int primary key, name text); 
create table foogroups (id int primary key, name text); 
create table foousergrps (user_id int unique references foousers not null, group_id int unique references foogroups not null); 
alter table foogroups add foreign key (id) references foousergrps (group_id) deferrable initially deferred; 
alter table foousers add foreign key (id) references foousergrps (user_id) deferrable initially deferred; 
begin; 
insert into foousers values (0, 'root'); 
insert into foousers values (1, 'daemon'); 
insert into foogroups values (0, 'wheel'); 
insert into foogroups values (1, 'daemon'); 
insert into foousergrps values (0,0); 
insert into foousergrps values (1,1); 
commit; 

:(非遅延可能、BOO)チェック機能の

insert into foousers values (2, 'bad'); 
insert into foousergrps values (2,2); 

例:

create table foousergrps (user_id int unique references foousers not null, group_id int not null); 
create function fooorphangroupcheck(int) returns boolean as $$ 
declare 
    gid alias for $1; 
begin 
    perform 1 from foousergrps where group_id = gid limit 1; 
    if NOT FOUND then return false; 
    end if; 
    return true; 
end; 
$$ 
LANGUAGE 'plpgsql'; 
alter table foogroups add check (fooorphangroupcheck(id)); 
+0

説明をもう一度お読みください。それはuser_in_groupに挿入することなくユーザを挿入することです。 user_in_groupからの削除や無効な参照の使用に関するものではありません。 –

+0

@Appelsien S:もう一度私の答えをお読みください。私は制約が間違った方法で動作していて、孤立したユーザーが許可されないように修正したという例を追加した直後に指摘しました。(間違ったuser_in_groupエントリは禁止します。 alter tableが必要になります)。 –

+0

改訂版では、foousergrpsのuser_idとgroup_idは一意になりました。 foousergrpsはN対Nの関係です。あなたはそれを1-1の関係にしました。 –

15

はあなたに見ていました今、対称的な制約のために改訂された

DEFERRABLE (INITIALLY DEFERRED)オプション付きですか?

+0

+1、および外部キー、ユニークおよび除外制約と同じ '遅延可能な初期遅延 ' –

+0

ありがとうございます。 DEFERRABLEを制約トリガーに設定できることはわかりませんでした。これは私が思うように私の質問を解決します。 –

+1

彼らがこの作業をしている場合、なぜ世界でCHECK()制約を延期することができないのですか?まあ、良い答え。 –

0

あなたはこのように、SQL WITH演算子を使用することができます。

WITH insert_user AS (
    INSERT INTO users(name) VALUES ('bla-bla-user') RETURNING id 
) 
INSERT INTO user_in_group(user_id, group_id) 
    SELECT id, 999 FROM insert_user UNION 
    SELECT id, 888 FROM insert_user; 

SELECT groups (id, name) user_in_group (user_id, group_id) 
関連する問題