2017-08-04 24 views
0

基本的に私はtable_aとtable_bを持っています。 table_bはtable_aのフィーチャで作成され、一般的にはsection_idの列とステータスを共有します。それはユニークですが、複数のsection_idsをtable_b持つことができますが、彼らはすべての共有と同じ状態別のテーブルが挿入または更新されたときに、テーブル内のカラムを更新するPostgresqlトリガ

ユーザーの挿入および更新table_bと私は状況をキャプチャするバックtable_aの変化よう

table_aのSECTION_IDが主キーです

CREATE TRIGGER table_b_aiu 
AFTER INSERT OR UPDATE 
ON table_b 
FOR EACH ROW 
WHEN (((new.status = 100) OR (new.status = 200))) 
EXECUTE PROCEDURE table_b_aiu(); 

CREATE OR REPLACE FUNCTION table_b_aiu() 
RETURNS trigger AS 
$BODY$ 
BEGIN 
UPDATE table_a a 
SET status = 100 
FROM table_b b 
WHERE (b.status = 100 or b.status = 200) 
AND a.section_id = b.section_id; 
    RETURN new; 
END; 
$BODY$ 
LANGUAGE plpgsql VOLATILE 
COST 100; 

この問題は、新しく更新または挿入された行だけを更新するのではなく、意図していない全データセットを更新することです。新しい行だけを更新するには?私はtrigerに引数を入れて試してみたが、それはsection_idが、それは十分なはずtable_aの主キーであるまで

CREATE OR REPLACE FUNCTION table_b_aiu() 
RETURNS trigger AS 
$BODY$ 
BEGIN 
    UPDATE table_a 
    SET status = 100 
    WHERE section_id = NEW.section_id 
    RETURN new; 
END; 
$BODY$ 

(ただしテストしていません)この機能を試してみてくださいnew.section_id

+0

は 'section_id'ユニークです。行の識別子?thの定義を持つと便利です問題のテーブルも同様です。 – cole

+0

@ C.Arendt section_idはtable_aの主キーですが、table_bのキーはありません – Luffydude

+1

トリガの引数はどういう意味ですか?次のようにします: 'EXECUTE PROCEDURE table_b_aiu(new.section_id);'? 引数として渡さずにその行にアクセスできます。 'a.section_id = new.section_id'を実行して、' table_b'から不要な部分を削除するだけです。 –

答えて

1

だから、いくつかのことがあります。このタイプの質問では、テストのためにテーブル定義にアクセスすることは間違いありません。私はテーブルデザインになると期待しているものを嘲笑した。また、「希望の動作」を含めると便利です(つまり、ステータスを常に100に設定しようとしていますか?table_bの動作に関係なく)。また、updateステートメントは必ずtable_a全体に当てはまるので、ユニークなレコードを取得するには一意制約が必要です。 SET status = new.statusのときにステータスを100に誤って設定すると、それは別の問題になります(ただし、更新は「テーブル全体に当たっているようです」、下記の例を参照してください - 特にsection_id=3)。

以下に、正確な動作を示します。私はあなたのトリガがstatus=new.statusを設定する必要があることを不審午前:

CREATE TABLE table_a 
(
section_id serial 
, status integer 
, CONSTRAINT pk_table_a PRIMARY KEY (section_id) 
); 

CREATE TABLE table_b 
(
id serial 
, section_id integer 
, status integer 
, CONSTRAINT pk_table_b_aiu PRIMARY KEY (id) 
) 
; 


CREATE OR REPLACE FUNCTION table_b_aiu() 
RETURNS trigger AS 
$BODY$ 
BEGIN 
UPDATE table_a a 
SET status = 100 -- intentional?? 
WHERE (new.status = 100 or new.status = 200) 
AND a.section_id = new.section_id; 

    RETURN new; 
END; 
$BODY$ 
LANGUAGE plpgsql VOLATILE 
COST 100; 

CREATE TRIGGER table_b_aiu 
AFTER INSERT OR UPDATE 
ON table_b 
FOR EACH ROW 
WHEN (((new.status = 100) OR (new.status = 200))) 
EXECUTE PROCEDURE table_b_aiu(); 


INSERT INTO table_a (section_id, status) 
values (1,100) 
, (2,200) 
, (3,201) 
, (4, 202) 
returning *; 

| section_id | status | 
| 1 | 100 | 
| 2 | 200 | 
| 3 | 201 | 
| 4 | 202 | 

INSERT INTO table_b (section_id, status) 
values (1,101), (2,100), (3,200), (4,201) 
returning *; 

| id | section_id | status | 
| 1 | 1 | 101 | 
| 2 | 2 | 100 | 
| 3 | 3 | 200 | 
| 4 | 4 | 201 | 

select * 
from table_a; 

| section_id | status | 
| 1 | 100 | 
| 4 | 202 | 
| 2 | 100 | 
| 3 | 100 | 

注:new.status in (100,200)が冗長であるが、私はあなたが安全であることを望んでいたと推定(場合には、誰かがこれまでwhen声明ずにトリガーを設定することであった

私の提案は:あなたが200分の100にテーブル自体のステータスオプションを制限したい場合は、私はそれらのオプションを持っているstatusテーブルへの外部キーを作成することをお勧め

+0

さて、意図された動作は何があってもtable_aにステータス100を残すことです。table_bの行が無効で削除されても、それが別のトリガーだと思うのは、100から何かにステータスを戻す唯一の機会でしょう。 – Luffydude

+1

あなたは間違いなく同じトリガーにそのロジックを入れることができます。一般的には、トリガーを避けるような方法でリレーショナル・デザインを設計するのであれば、それが望ましいとはいえ、それが望ましいと思います。上記はあなたが期待しているように機能しますか? (テストする必要がある場合は、 'SET search_path = otherschema;'でテーブル定義済みのスキーマを避けることができます)。 – cole

+1

もう1つの考え方 - ユーザーがログテーブルに挿入して親​​を更新していること。ユーザーが親テーブルに挿入したり、親テーブルを更新したり、トリガーを使用してログテーブルを作成したりすることができます(これは通常のアプローチです)。 2番目から最後の例[here](https://www.postgresql.org/docs/9.4/static/sql-createtrigger.html)を参照してください。 – cole

1

を受け付けません。

関連する問題