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

Sql 如何保存通过触发器的字符中带有逗号的数据?,sql,postgresql,plpgsql,database-trigger,Sql,Postgresql,Plpgsql,Database Trigger,我有一个字符类型可变的字段,但在尝试保存包含小数的数据时出错。我想毫无问题地保存这些数据 这是我的触发器: 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;

我有一个字符类型可变的字段,但在尝试保存包含小数的数据时出错。我想毫无问题地保存这些数据

这是我的触发器:

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;

我的桌子通常是:

错误:

您可以使用
format()
创建动态SQL查询,因为它将自动正确处理标识符和文本。人们通常忽略的一点是,您可以使用
(…).*
将单个记录表达式扩展到其所有列-这也适用于
新的
旧的
记录触发器中的变量,例如
选择(新)。*

您还可以使用
execute
语句的关键字将变量传递给动态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, ($1).*';

    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并重新使用它。这些“参数”被
format()
函数替换(该函数保留
$1

注意在SQL字符串中使用了
($1)。*
。这将使
execute
语句将记录参数
$1
扩展到其所有列。使用
关键字通过
以“本机”方式传递记录本身


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


如果您不在审计表上运行大规模报告(在这种情况下,使用显式列名会更有效),您可能需要考虑使用
JSON
HSTORE
列来存储整个记录的更通用的审计触发器。有几个现成的审核触发器可用:


您需要展示一些示例。例如,您的代码是什么样子的?字符串中使用逗号没有问题。当您保存一条记录时,该记录通过触发器保存副本。这里有个问题。大家好,欢迎来到堆栈溢出。您能告诉我们您正在运行的确切SQL、任何触发器以及您得到的确切错误吗?然后我们可以更好地回答您的问题。我已经更新了这个问题。错误看起来很清楚-您可以尝试smth,比如
插入到t(c1,c2)中选择c1,c2,c3,c4,c5
返回列的数量大于目标列的数量