如何检查 Postgres 触发器函数中是否存在旧列
How to check if OLD column exist in Postgres Trigger Function
我想创建一个已删除的日志并从旧行列插入数据。问题是每个 table 的列都不相同,一些 tables 只有 transaction_date
而其他 table 只有 created_at
。所以我想检查 transaction_date
是否只使用它,否则使用 created_at
列。我尝试使用 coalesce
函数,但仍然 return:
ERROR: record "old" has no field "transaction_date" CONTEXT: SQL
statement "INSERT INTO "public"."delete_logs" ("table", "date") VALUES
(TG_TABLE_NAME, coalesce(OLD.transaction_date,
coalesce(OLD.created_at, now())))" PL/pgSQL function delete_table()
line 2 at SQL statement
这是我的函数:
CREATE OR REPLACE FUNCTION delete_table() RETURNS trigger AS
$$BEGIN
INSERT INTO "public"."deleted_logs" ("table", "created_at") VALUES (TG_TABLE_NAME, coalesce(OLD.transaction_date, coalesce(OLD.created_at, now())));
RETURN OLD;
END;$$ LANGUAGE plpgsql;
CREATE TRIGGER "testDelete" AFTER DELETE ON "exampletable" FOR EACH ROW EXECUTE PROCEDURE "delete_table"();
其实我想为每个table创建一个函数,但是我觉得以后很难更新这个函数,所以我需要为所有[=24=创建一个函数]s.
So I want to check if transaction_date
just use it, otherwise use created_at
column.
您可以通过将行转换为 json:
来避免您看到的异常
CREATE OR REPLACE FUNCTION log_ts_after_delete()
RETURNS trigger
LANGUAGE plpgsql AS
$func$
BEGIN
INSERT INTO public.deleted_logs
(table_name , created_at) -- "table" is a reserved word
VALUES (TG_TABLE_NAME, COALESCE(to_json(OLD)->>'transaction_date', to_json(OLD)->>'created_at')::timestamptz);
RETURN NULL; -- not used in AFTER trugger
END
$func$;
我的回答假定 transaction_date
定义为 NOT NULL
。否则,表达式默认为 created_at
。可能不是你想要的。
JSON 没有 SQL 严格。对不存在的 JSON 键的引用导致 NULL 而不是对不存在的 table 列的引用的异常。所以 COALESCE
就可以了。
相关:
- How to set value of composite variable field using dynamic SQL
如果行很宽,只转换一次 JSON 并将其保存到变量中,或者在子查询或 CTE 中进行转换可能更便宜。
相关:
如果 table 从不切换有问题的列,则在触发器定义中传递参数会 便宜得多 。
您发现(在创建触发器时)一次 具有:
SELECT attname
FROM pg_attribute
WHERE attrelid = 'public.exampletable'::regclass
AND attname IN ('transaction_date', 'created_at')
AND NOT attisdropped
ORDER BY attname DESC
此returns 'transaction_date' 如果table中存在这样的列,else 'created_at',else NULL(无行)。相关:
- PostgreSQL rename a column only if it exists
每种类型的触发器都有一个单独的触发器功能仍然是最便宜的。只有两个功能而不是一个。如果经常触发触发器,我会做 that.
如果您可以,则避免异常处理。 The manual:
Tip
A block containing an EXCEPTION
clause is significantly more
expensive to enter and exit than a block without one. Therefore, don't
use EXCEPTION
without need.
我想创建一个已删除的日志并从旧行列插入数据。问题是每个 table 的列都不相同,一些 tables 只有 transaction_date
而其他 table 只有 created_at
。所以我想检查 transaction_date
是否只使用它,否则使用 created_at
列。我尝试使用 coalesce
函数,但仍然 return:
ERROR: record "old" has no field "transaction_date" CONTEXT: SQL statement "INSERT INTO "public"."delete_logs" ("table", "date") VALUES (TG_TABLE_NAME, coalesce(OLD.transaction_date, coalesce(OLD.created_at, now())))" PL/pgSQL function delete_table() line 2 at SQL statement
这是我的函数:
CREATE OR REPLACE FUNCTION delete_table() RETURNS trigger AS
$$BEGIN
INSERT INTO "public"."deleted_logs" ("table", "created_at") VALUES (TG_TABLE_NAME, coalesce(OLD.transaction_date, coalesce(OLD.created_at, now())));
RETURN OLD;
END;$$ LANGUAGE plpgsql;
CREATE TRIGGER "testDelete" AFTER DELETE ON "exampletable" FOR EACH ROW EXECUTE PROCEDURE "delete_table"();
其实我想为每个table创建一个函数,但是我觉得以后很难更新这个函数,所以我需要为所有[=24=创建一个函数]s.
So I want to check if
transaction_date
just use it, otherwise usecreated_at
column.
您可以通过将行转换为 json:
来避免您看到的异常CREATE OR REPLACE FUNCTION log_ts_after_delete()
RETURNS trigger
LANGUAGE plpgsql AS
$func$
BEGIN
INSERT INTO public.deleted_logs
(table_name , created_at) -- "table" is a reserved word
VALUES (TG_TABLE_NAME, COALESCE(to_json(OLD)->>'transaction_date', to_json(OLD)->>'created_at')::timestamptz);
RETURN NULL; -- not used in AFTER trugger
END
$func$;
我的回答假定 transaction_date
定义为 NOT NULL
。否则,表达式默认为 created_at
。可能不是你想要的。
JSON 没有 SQL 严格。对不存在的 JSON 键的引用导致 NULL 而不是对不存在的 table 列的引用的异常。所以 COALESCE
就可以了。
相关:
- How to set value of composite variable field using dynamic SQL
如果行很宽,只转换一次 JSON 并将其保存到变量中,或者在子查询或 CTE 中进行转换可能更便宜。
相关:
如果 table 从不切换有问题的列,则在触发器定义中传递参数会 便宜得多 。 您发现(在创建触发器时)一次 具有:
SELECT attname
FROM pg_attribute
WHERE attrelid = 'public.exampletable'::regclass
AND attname IN ('transaction_date', 'created_at')
AND NOT attisdropped
ORDER BY attname DESC
此returns 'transaction_date' 如果table中存在这样的列,else 'created_at',else NULL(无行)。相关:
- PostgreSQL rename a column only if it exists
每种类型的触发器都有一个单独的触发器功能仍然是最便宜的。只有两个功能而不是一个。如果经常触发触发器,我会做 that.
如果您可以,则避免异常处理。 The manual:
Tip
A block containing an
EXCEPTION
clause is significantly more expensive to enter and exit than a block without one. Therefore, don't useEXCEPTION
without need.