2017-08-30 7 views
0

私は問題になり始めたpgsqlログトリガーを最適化しようと多くの時間を費やしました。私は巨大な進歩を遂げました(3分行を挿入することで18分から2.5分に)しかし、いくつかのpgSqlマスターがそれをさらに良くすることができるかどうかを知りたいと思います。PostgreSQLのログトリガーの最適化

CREATE OR REPLACE FUNCTION table_log_trig() 
    RETURNS trigger AS 
$BODY$ 
    DECLARE 
     col TEXT; -- Single column name to save 
     newVal TEXT; -- New value for column 
     oldVal TEXT; -- Old value for column 
     colLimit TEXT[]; -- Columns that should be logged 
    BEGIN 
     IF TG_ARGV[0] IS NOT NULL THEN 
     -- Trigger specifies columns to log 
      SELECT array_agg(unnest) 
      FROM unnest(string_to_array(TG_ARGV[0], ',')) 
      INTO colLimit; 
     ELSE 
     -- Trigger with no params. Log all columns 
      SELECT array_agg(json_object_keys) 
      FROM json_object_keys(row_to_json(NEW)) 
      WHERE json_object_keys NOT IN ('id', 'created_at', 'updated_at') -- Exceptions 
      INTO colLimit; 
     END IF; 

     -- Loop over columns that should be saved in log 
     FOREACH col IN ARRAY colLimit 
     LOOP 
      -- INSERT & UPDATE 
      EXECUTE 'SELECT ($1).' || col || '::text' INTO newVal USING NEW; 
      -- UPDATE 
      IF TG_OP = 'UPDATE' THEN 
       EXECUTE 'SELECT ($1).' || col || '::text' INTO oldVal USING OLD; 
      END iF; 
      -- Add only new or changed data 
      IF 
       newVal != oldVal OR 
       (oldVal IS NULL AND newVal IS NOT NULL) OR 
       (oldVal IS NOT NULL AND newVal IS NULL) 
      THEN 
       INSERT INTO tab_logs (record_id, field_name, old_value, new_value, created_at, created_by, action) 
       VALUES (NEW.id, col, oldVal, newVal, NOW(), 999, 'O'); 
      END IF; 
     END LOOP; 

     RETURN NEW; 
    END; 
$BODY$ 
    LANGUAGE plpgsql VOLATILE 
    COST 100; 
+0

だけの提案が、あなたから変更することができた場合:!newValに= OLDVAL OR (OLDVALはIS NULLとnewValはNULLではありません)または (oldVal IS NOT NULLとnewVal IS NULL)を単にisnull(newVal、 '')<> oldVal – jimmy8ball

答えて

1

row_to_json()は、列名と値の両方を返します。後で動的SQLを使用してこれらの値を抽出するのではなく、これらの値を使用することもできます。

私は徹底的にこれをテストしていない、ましてやそれをベンチマークが、ここでの要点です:

CREATE OR REPLACE FUNCTION table_log_trig() RETURNS trigger AS 
$$ 
DECLARE 
    OldJson JSONB = NULL; 
BEGIN 
    IF TG_OP <> 'INSERT' THEN 
    OldJson := to_jsonb(old); 
    END IF; 

    INSERT INTO tab_logs (record_id, field_name, old_value, new_value, created_at, created_by, action) 
    SELECT new.id, key, OldValues.value, NewValues.value, now(), 999, 'O' 
    FROM jsonb_each(to_jsonb(new)) NewValues 
    LEFT JOIN jsonb_each(OldJson) OldValues USING (key) 
    WHERE 
    (
     (TG_ARGV[0] IS NULL AND key NOT IN ('id', 'created_at', 'updated_at')) OR 
     (TG_ARGV[0] IS NOT NULL AND key = ANY(string_to_array(TG_ARGV[0], ','))) 
    ) AND 
    OldValues.value::text IS DISTINCT FROM NewValues.value::text; 

    RETURN NULL; 
END 
$$ 
LANGUAGE plpgsql VOLATILE; 
+0

WOW! 16k - > 8s、100k行!したがって、あなたの命題は2倍速くなります。印象的な。ありがとうございました! – imclickingmaniac

+0

ビルドされた文字列に対してEXECUTEを実行せずにINSERT INTO log_ {TG_TABLE_SCHEMA.TG_TABLE_NAME}オプションがありますか? – imclickingmaniac

+0

@imclickingmaniac:そうではありません。別の言語(例えばC)では、再解析を避けるために準備された 'INSERT'文のキャッシュを保持することができますが、それが問題になるかどうかはわかりません。より簡単な解決法は、各テーブルに対して異なるトリガ機能を作成することです。 –

関連する問題