Postgresql 创建日志模式
Postgresql create a log schema
所以我的问题很简单。我有一个架构 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 || ',.' || 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"
instead of
INSERT INTO log.user SELECT 2,NULL,NULL,"2015-10-28 13:52:44.785947"
想把 ",,"
转换成 ",NULL,"
吗?
谢谢
-昆汀
首先,我必须说,在我看来,使用 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
好多少,但这是您的决定。
所以我的问题很简单。我有一个架构 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 || ',.' || 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" instead of INSERT INTO log.user SELECT 2,NULL,NULL,"2015-10-28 13:52:44.785947"
想把 ",,"
转换成 ",NULL,"
吗?
谢谢
-昆汀
首先,我必须说,在我看来,使用 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
好多少,但这是您的决定。