web-dev-qa-db-ja.com

プレーンSQLのトリガー関数でOLD / NEWテーブル値にアクセスする

アイテムが変更されるたびに親注文値を自動的に更新するトリガー関数を作成しようとしています。

これは私が作成したものです:

CREATE OR REPLACE FUNCTION update_totals() RETURNS void AS $$
    UPDATE orders SET
       total_fees = (SELECT SUM(fees) FROM order_items WHERE order_id = OLD.order_id),
       total_profit = (CASE WHEN total_cost IS NOT NULL THEN total - total_tax - total_cost - (SELECT SUM(fees) FROM order_items WHERE order_id = OLD.order_id) ELSE NULL END)
    WHERE id = OLD.order_id;
$$
LANGUAGE SQL;

CREATE TRIGGER update_totals AFTER INSERT 
OR UPDATE OF fees
ON order_items INITIALLY DEFERRED
FOR EACH ROW
WHEN ( OLD.* IS DISTINCT FROM NEW.* ) EXECUTE PROCEDURE update_totals();

これを実行しようとすると、次のエラーが発生します。

missing FROM-clause for table "old"

トリガーで元のテーブルへの参照を作成してみました。

CREATE TRIGGER update_totals AFTER INSERT 
OR UPDATE OF fees
ON order_items
REFERENCING OLD ROW AS old_order
DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW
WHEN ( OLD.* IS DISTINCT FROM NEW.* ) EXECUTE PROCEDURE update_totals();

しかし、それはエラーsyntax error at or near "REFERENCING"を与えるだけです。

元のorder_idテーブルからorder_itemsを取得するにはどうすればよいですか?

5
eComEvo

まず、_order_items_テーブルにトリガーを作成するには、update_totals()トリガー関数でなければなりません which _RETURNS TRIGGER_(boldは私のものです)。

データ変更トリガーは、引数なしの関数として宣言され、戻りタイプのトリガーです。 CREATE TRIGGERで指定された引数を受け取ることを想定している場合でも、関数は引数なしで宣言する必要があることに注意してください。

そうでない場合は、エラー_ERROR: function func_order_items must return type trigger_が発生します。

2番目に、トリガーで_AFTER INSERT_を使用する場合、OLDを使用できないことに注意してください。そうでない場合は、エラー_ERROR: record "old" is not assigned yet_が発生します。

最後に、以下の例を見てください:

_CREATE TABLE IF NOT EXISTS orders(ID INT NOT NULL PRIMARY KEY, X INT);
CREATE TABLE IF NOT EXISTS order_items (ID INT NOT NULL PRIMARY KEY, ORDER_ID INT, Y INT); 

INSERT INTO orders VALUES(1, 0);
INSERT INTO order_items VALUES(10, 1, 20);
INSERT INTO order_items VALUES(11, 1, 30);

CREATE OR REPLACE FUNCTION func_order_items() RETURNS trigger AS 
$$
BEGIN
  IF (TG_OP = 'UPDATE') THEN
    UPDATE orders
    SET X = (SELECT SUM(Y) FROM order_items WHERE order_id = OLD.order_id)
    WHERE ID = OLD.order_id;
  ELSIF (TG_OP = 'INSERT') THEN
    UPDATE orders
    SET X = (SELECT SUM(Y) FROM order_items WHERE order_id = NEW.order_id)
    WHERE ID = NEW.order_id;
  END IF;
  RETURN NULL;
END
$$
LANGUAGE PLPGSQL;

CREATE TRIGGER trigger_order_items 
AFTER INSERT OR UPDATE 
ON order_items 
FOR EACH ROW EXECUTE PROCEDURE func_order_items();

UPDATE order_items SET Y = 200 WHERE ID = 10;
INSERT INTO order_items VALUES (12, 1, 200);
_
5
Luan Huynh