Postgresql 创建日志模式
Posted
技术标签:
【中文标题】Postgresql 创建日志模式【英文标题】:Postgresql create a log schema 【发布时间】:2016-01-29 08:02:16 【问题描述】:所以我的问题很简单。我有一个架构prod
有很多表,另一个log
有完全相同的表和结构(主键改变就是这样)。
当我在架构prod
中执行UPDATE
或DELETE
时,我想在log
架构中记录旧数据。
我在更新或删除后调用了以下函数:
CREATE FUNCTION prod.log_data() RETURNS trigger
LANGUAGE plpgsql AS $$
DECLARE
v RECORD;
column_names text;
value_names text;
BEGIN
-- get column names of current table and store the list in a text var
column_names = '';
value_names = '';
FOR v IN SELECT * FROM information_schema.columns WHERE table_name = quote_ident(TG_TABLE_NAME) AND table_schema = quote_ident(TG_TABLE_SCHEMA) LOOP
column_names = column_names || ',' || v.column_name;
value_names = value_names || ',$1.' || v.column_name;
END LOOP;
-- remove first char ','
column_names = substring( column_names FROM 2);
value_names = substring( value_names FROM 2);
-- execute the insert into log schema
EXECUTE 'INSERT INTO log.' || TG_TABLE_NAME || ' ( ' || column_names || ' ) VALUES ( ' || value_names || ' )' USING OLD;
RETURN NULL; -- no need to return, it is executed after update
END;$$;
烦人的部分是我必须从information_schema
获取每一行的列名。
我宁愿用这个:
EXECUTE 'INSERT INTO log.' || TG_TABLE_NAME || ' SELECT ' || OLD;
但有些值可以是NULL
,所以这会执行:
INSERT INTO log.user SELECT 2,,,"2015-10-28 13:52:44.785947" 代替 INSERT INTO log.user SELECT 2,NULL,NULL,"2015-10-28 13:52:44.785947"
有没有办法将",,"
转换为",NULL,"
?
谢谢
-昆汀
【问题讨论】:
【参考方案1】:首先我必须说,在我看来,使用 PostgreSQL 系统表(如information_schema
)是这种用例的正确方法。特别是您必须编写一次:您创建函数prod.log_data()
并完成。此外,在这种情况下使用OLD
(就像*
)可能会很危险,因为没有指定元素顺序。
但是,
要回答您的确切问题,我知道的唯一方法是对OLD
进行一些操作。只需观察您通过连接... ' SELECT ' || OLD
将OLD
转换为text
。默认转换会创建丑陋的双逗号。因此,接下来您可以使用该文本。最后我建议:
DECLARE
tmp TEXT
...
BEGIN
...
/*to make OLD -> text like (2,,3,4,,)*/
SELECT '' || OLD INTO tmp; /*step 1*/
/*take care of commas at the begining and end: '(,' ',)'*/
tmp := replace(replace(tmp, '(,', '(NULL,'), ',)', ',NULL)'); /*step 2*/
/* replace rest of commas to commas with NULL between them */
SELECT array_to_string(string_to_array(tmp, ',', ''), ',', 'NULL') INTO tmp; /*step 3*/
/* Now we can do EXECUTE*/
EXECUTE 'INSERT INTO log.' || TG_TABLE_NAME || ' SELECT ' || tmp;
当然你可以一步完成1-3步
SELECT array_to_string(string_to_array(replace(replace('' || NEW, '(,', '(NULL,'), ',)', ',NULL)'), ',', ''), ',', 'NULL') INTO tmp;
在我看来,这种方法并没有比使用 information_schema
更好,但这是你的决定。
【讨论】:
非常感谢!感谢您的意见,我将在大数据库上进行一些测试,看看information_schema
是否对我的用例来说太慢了。
其实这是为了性能。但是你能告诉我postgres什么时候触发吗?我想知道 postgre 是否在触发触发器 after INSERT
之前返回到 php
我不是 100% 肯定,但我怀疑它是。 PostgreSQL 中的每个触发器(无论是AFTER
还是BEFORE
)都在与主查询相同的事务中执行。这意味着在触发器结束之前执行不会结束。请注意,AFTER
触发器中未处理的异常会使整个事务回滚。因此,如果 php 认为即使 db 服务器本身还没有新的查询成功,也可能会出现问题。以上是关于Postgresql 创建日志模式的主要内容,如果未能解决你的问题,请参考以下文章
尝试通过 liquibase、postgresql、spring boot 创建新数据库。结果->“数据库更改日志”不存在