如何保存通过触发器传递的字符变化中带有逗号的数据?

How to save a data with comma in character varying that passes through a trigger?

我有一个类型为 character varying 的字段,但在尝试保存包含小数的数据时出现错误。我想毫无问题地保存该数据。

这是我的触发器:

CREATE TRIGGER "public.usuarios_trigger_process_audit"


BEFORE INSERT OR UPDATE OR DELETE
  ON usuarios

FOR EACH ROW
  EXECUTE PROCEDURE process_audit();

这是程序:

DECLARE
    newtable text;
    col information_schema.columns %ROWTYPE;
    txtquery text;
    line_old TEXT;
    tmpquery text;
    i int;
    columns_old text[];
BEGIN
    IF ( TG_TABLE_SCHEMA = 'public' ) THEN
    SELECT TG_TABLE_NAME || '_actividad' INTO newtable;    /*  select TG_RELNAME || '_actividad' into newtable; */
    ELSE
    SELECT TG_TABLE_SCHEMA || '_' || TG_TABLE_NAME || '_actividad' INTO newtable;    /*  select TG_RELNAME || '_actividad' into newtable; */
    END IF;

    PERFORM creartablaactividad( TG_TABLE_SCHEMA, TG_TABLE_NAME );

    IF ( TG_OP = 'DELETE' ) THEN
    line_old := TRIM( substr(OLD::text,2,(select length(OLD::text)-2)) );
    columns_old := STRING_TO_ARRAY( line_old, ',' );
    i := 0;
    tmpquery := '''' || array_to_string(columns_old, ''',''') || '''';
    tmpquery := replace(tmpquery,','''',',',NULL,');
        /* SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, inet_client_addr(), now (), ''D'',' || replace(tmpquery, ',''''',',NULL') into txtquery; */
        SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, now (), ''D'',' || replace(tmpquery, ',''''',',NULL') into txtquery;
        EXECUTE txtquery;
        RETURN OLD;
    ELSIF ( TG_OP = 'UPDATE' ) THEN
    line_old := TRIM( substr(OLD::text,2,(select length(OLD::text)-2)) );
        columns_old := STRING_TO_ARRAY( line_old, ',' );
        i := 0;
        tmpquery := '''' || array_to_string(columns_old, ''',''') || '''';
        tmpquery := replace(tmpquery,','''',',',NULL,');
        tmpquery := replace(tmpquery,','''',',',NULL,');
        /* SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, inet_client_addr(), now (), ''ANT'',' || replace(tmpquery, ',''''',',NULL') into txtquery; */
        SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, now (), ''ANT'',' || replace(tmpquery, ',''''',',NULL') into txtquery;
        EXECUTE txtquery;
        line_old := TRIM( substr(NEW::text,2,(select length(NEW::text)-2)) );
        columns_old := STRING_TO_ARRAY( line_old, ',' );
        i := 0;
        tmpquery := '''' || array_to_string(columns_old, ''',''') || '''';
        tmpquery := replace(tmpquery,','''',',',NULL,');
        /* SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, inet_client_addr(), now (), ''U'',' || replace(tmpquery, ',''''',',NULL') into txtquery; */
        SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, now (), ''U'',' || replace(tmpquery, ',''''',',NULL') into txtquery;
        EXECUTE txtquery;
        RETURN NEW;
    ELSIF ( TG_OP = 'INSERT' ) THEN
    line_old := TRIM( substr(NEW::text,2,(select length(NEW::text)-2)) );
        columns_old := STRING_TO_ARRAY( line_old, ',' );
        i := 0;
        tmpquery := '''' || array_to_string(columns_old, ''',''') || '''';
        tmpquery := replace(tmpquery,','''',',',NULL,');
        /* SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, inet_client_addr(), now (), ''I'',' || replace(tmpquery, ',''''',',NULL') into txtquery; */
        SELECT 'INSERT INTO actividad.' || newtable ||' SELECT user, now (), ''I'',' || replace(tmpquery, ',''''',',NULL') into txtquery;
        EXECUTE txtquery;
        RETURN NEW;
    END IF;

    RETURN NULL; -- result is ignored since this is an AFTER trigger
END;

我的table习惯:

错误:

您可以使用 format() 来更轻松地创建动态 SQL 查询,因为它会自动正确处理标识符和文字。人们通常忽略的一件事是,您可以使用 (...).* 将单个记录表达式扩展到它的所有列——这也适用于触发器中的 NEWOLD 记录变量,例如select (new).*

您还可以使用 execute 语句的 using 关键字将变量传递给动态 SQL。无需在记录和文本表示之间来回转换记录。

利用这种可能性,您的触发函数可以简化为:

DECLARE 
  l_sql text;
BEGIN
    IF TG_TABLE_SCHEMA = 'public' THEN
      newtable := TG_TABLE_NAME || '_actividad';
    ELSE
      newtable := TG_TABLE_SCHEMA || '_' || TG_TABLE_NAME || '_actividad';
    END IF;

    PERFORM creartablaactividad(TG_TABLE_SCHEMA, TG_TABLE_NAME);
    l_sql := 'INSERT INTO actividad.%I  SELECT current_user, current_timestamp, %L, ().*';

    IF TG_OP = 'DELETE' THEN
      execute format(l_sql, newtable, 'D') using OLD;
      RETURN OLD;
    ELSE
      -- covers UPDATE and INSERT
      execute format(l_sql, newtable, 'U') using NEW;
      RETURN NEW;
    END IF;

    RETURN NULL; -- result is ignored since this is an AFTER trigger
END;

使用像 %I%L 这样的占位符也可以只定义一次实际的 SQL 并重新使用它。那些 "parameters" 被替换为 format() 函数(它保留了 </code>)</p> <p>注意 SQL 字符串中 <code>().* 的使用。这将使 execute 语句将记录参数 </code> 扩展到它的所有列。记录本身通过 "natively" 和 <code>USING 关键字传递。


在没有目标列列表的情况下使用 INSERTinsert into some_table ... 而不是 insert into some_table (col1, col2, ...) ...)是一件非常脆弱的事情。如果源和目标不匹配,插入很容易失败。 .


如果您不 运行 对审计表进行大量报告(其中具有明确的列名会更有效率),您可能需要考虑使用 JSONHSTORE 列来存储整个记录。有几个现成的审计触发器可用: