Postgresql 创建日志模式

Postgresql create a log schema

所以我的问题很简单。我有一个架构 prod 包含许多表,另一个架构 log 具有完全相同的表和结构(主键改变就是这样)。 当我在模式 prod 中执行 UPDATEDELETE 时,我想在 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 ' || OLDOLD 转换为 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 好多少,但这是您的决定。