Postgres触发器函数中如何检查OLD记录是否存在指定列
问题原因
报错的核心原因是PL/pgSQL会在函数执行前的静态校验阶段检查OLD记录的字段是否存在,coalesce属于运行时逻辑,无法绕过编译阶段的字段存在性校验,当绑定触发器的表没有transaction_date字段时,函数校验会直接失败抛出错误。
通用触发器函数实现方案
我们可以通过将OLD记录转为jsonb对象,动态判断字段是否存在再取值,即可绕过静态校验,实现多表适配:
CREATE OR REPLACE FUNCTION delete_table() RETURNS trigger AS $$ DECLARE old_row_json jsonb := to_jsonb(OLD); log_date timestamptz; BEGIN -- 优先取transaction_date,不存在则取created_at,都不存在取当前时间 IF old_row_json ? 'transaction_date' THEN log_date := (old_row_json ->> 'transaction_date')::timestamptz; ELSIF old_row_json ? 'created_at' THEN log_date := (old_row_json ->> 'created_at')::timestamptz; ELSE log_date := now(); END IF; INSERT INTO "public"."deleted_logs" ("table", "created_at") VALUES (TG_TABLE_NAME, log_date); RETURN OLD; END; $$ LANGUAGE plpgsql;
触发器绑定方式
所有需要记录删除日志的表都可以直接绑定同一个函数,不需要单独编写逻辑:
-- 示例:绑定到exampletable表 CREATE TRIGGER "testDelete" AFTER DELETE ON "exampletable" FOR EACH ROW EXECUTE PROCEDURE "delete_table"(); -- 其他表绑定同样复用上面的函数即可 CREATE TRIGGER "otherTableDelete" AFTER DELETE ON "other_table" FOR EACH ROW EXECUTE PROCEDURE "delete_table"();
方案说明
- 使用
to_jsonb(OLD)将行记录转为jsonb对象,避免静态字段校验 - 通过
?操作符判断jsonb中是否存在对应字段,适配不同表的字段差异 - 字段值的类型转换规则可根据实际业务调整,比如表中时间字段为date类型时,将
::timestamptz改为::date即可 - 后续如果需要调整时间取值优先级、新增日志字段,只需要修改这一个函数即可,所有绑定触发器的表都会自动生效,维护成本极低
内容的提问来源于stack exchange,提问作者Muhammad Dyas Yaskur
相关产品推荐
相关产品推荐

