2

所以我的问题很简单。我有一个prod包含许多表的模式,另一个log具有完全相同的表和结构(主键更改就是这样)。当我做UPDATEDELETE在模式中prod时,我想在模式中记录旧数据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,"

谢谢

-昆汀

4

1 回答 1

0

首先我必须说,在我看来,使用 PostgreSQL 系统表(如information_schema)是这种用例的正确方法。尤其是你必须编写一次:你创建函数prod.log_data()并完成。此外,由于未指定元素顺序,因此OLD在该上下文中使用(就像 一样)可能很危险。*

但是

要回答您的确切问题,我知道的唯一方法是对OLD. 只需观察你通过进行连接OLD来转换。默认转换会创建丑陋的双逗号。因此,接下来您可以使用该文本。最后我提议:text... ' SELECT ' || OLD

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,但这是你的决定。

于 2015-10-29T14:38:43.793 回答