私はpostgres9.1を使用していますが、後で更新したり、新しい行をビューに挿入したりできるビューを作成する方法について、いくつかのチュートリアルを見つけるのに苦労しています。
誰かが更新可能で新しい行の挿入を受け入れることができるビューを実行する方法についてのガイダンスを教えてもらえますか?
私はpostgres9.1を使用していますが、後で更新したり、新しい行をビューに挿入したりできるビューを作成する方法について、いくつかのチュートリアルを見つけるのに苦労しています。
誰かが更新可能で新しい行の挿入を受け入れることができるビューを実行する方法についてのガイダンスを教えてもらえますか?
更新可能なビューはやや複雑です。それらを行うには 2 つの方法があります。1 つは RULE を使用し、もう 1 つは TRIGGER を使用します。トリガーはより多くのパフォーマンスの問題を引き起こしますが、RULE はより微妙なコーディングの問題を引き起こします。ここでは両方のアプローチについて説明します。
ベース テーブルと、ベース テーブルの一部へのアクセスを提供するビューがあるとします。
CREATE TABLE mybase (
id serial primary key,
type_id int not null,
payload text not null
);
CREATE VIEW type1 AS select * from mybase WHERE type_id = 1;
わかりました、十分に簡単です。次に、ビューを更新可能にします。古い方法 (メンテナンスの落とし穴) は、ルールを使用することです。
CREATE RULE on_insert AS ON insert TO type1
DO INSTEAD INSERT into mybase(id, type_id, payload) VALUES (new.id, 1, payload);
CREATE RULE on_update AS ON update TO type1
DO INSTEAD UPDATE mybase set payload = new.payload where id = old.id;
これで動作しますが、いくつか注意点があります。1つ目は、次の場合です。
INSERT INTO type1 (id, type_id, payload) values (DEFAULT, 2, 'foo');
代わりに、2 ではなく 1 を静かに入力します。これは望ましくない場合があります。コードをより複雑にするか、関数を呼び出して挿入と更新を行うことができますが、RULE には計画時にクエリに解析されるという利点があり、関数は行ごとに 1 回実行されるため、100 万回更新する更新行は、関数よりも単純なルールの方がはるかに優れたパフォーマンスを発揮します。ルールを正しく設定するのは複雑で注意が必要ですが、それは、ビューへの更新が、ベース テーブルへの更新への書き込みと同じようにスケールアップすることを意味します。
ただし、複数のテーブルがある場合、複数の行を更新または削除し、これが (行ごとではなく) 計画時にマージされるため、事態ははるかに複雑になります。削除する必要があるものを見つけます。しかし、関数を使用すると、削除と更新で書き込みスケーリングの問題が発生します (これにより、基本的にインデックス スキャン ループが繰り返されます)。
もう 1 つのオプションは、トリガーを使用することです。トリガーは、書き込まれる行ごとに呼び出される関数であるため、関数を呼び出すルールの書き込みスケーリングの問題があります。このアプローチでは、トリガー関数を作成し、ビューにトリガーを作成します。
CREATE OR REPLACE FUNCTION type1_trigger_func() RETURNS trigger LANGUAGE PLPGSQL AS
$$
BEGIN
IF TG_OP = 'INSERT' THEN
IF new.type_id <> 1 THEN RAISE EXCEPTION 'Invalid type!'; END IF;
insert into mybasetable (id, type_id, payload)
VALUES (new.id, new.type_id, new.payload);
RETURN NEW;
ELSIF TG_OP = 'UPDATE' THEN
IF new.type_id <> 1 THEN RAISE EXCEPTION 'Invalid type!'; END IF;
-- update statement goes here
RETURN NEW;
ELSIF TG_IP = 'DELETE' THEN
DELETE FROM mybasetable WHERE id = old.id;
RETURN OLD;
END IF;
END;
$$;
次に、トリガーを作成します。
CREATE TRIGGER make_updateable INSTEAD OF INSERT OR UPDATE OR DELETE ON TYPE1
FOR EACH ROW EXECUTE PROCEDURE type1_trigger_func();
一般に、このコンテキストでの RULE と TRIGGER の難しさから、私はビューではなくストアド プロシージャに傾倒しました。