PostgreSQL日志触发器优化(PostgreSQL log trigger optimalization)

编程入门 行业动态 更新时间:2024-10-11 23:17:25
PostgreSQL日志触发器优化(PostgreSQL log trigger optimalization)

我花了很多时间尝试优化我们的pgsql日志触发器,这开始成为一个问题。 我做了很大的进步(通过插入3M行从18分钟到2.5分钟)但我想知道一些pgSql主人是否能够做得更好。

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;

I spent a lot of time trying to optimize our pgsql log trigger which started to be a problem. I did huge progress (from 18min to 2.5min by inserting 3M rows) but I would like to know if some pgSql masters will be able to do it even better.

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;

最满意答案

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;

row_to_json() returns both column names and values; you may as well make use of these values, rather than extracting them later via dynamic SQL.

I haven't thoroughly tested this, let alone benchmarked it, but here's the gist of it:

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;

更多推荐

本文发布于:2023-07-17 14:05:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1145689.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:触发器   日志   PostgreSQL   optimalization   trigger

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!