如何保存通过触发器传递的字符变化中带有逗号的数据?
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 查询,因为它会自动正确处理标识符和文字。人们通常忽略的一件事是,您可以使用 (...).*
将单个记录表达式扩展到它的所有列——这也适用于触发器中的 NEW
和 OLD
记录变量,例如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
关键字传递。
在没有目标列列表的情况下使用 INSERT
(insert into some_table ...
而不是 insert into some_table (col1, col2, ...) ...
)是一件非常脆弱的事情。如果源和目标不匹配,插入很容易失败。 .
如果您不 运行 对审计表进行大量报告(其中具有明确的列名会更有效率),您可能需要考虑使用 JSON
或 HSTORE
列来存储整个记录。有几个现成的审计触发器可用:
我有一个类型为 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 查询,因为它会自动正确处理标识符和文字。人们通常忽略的一件事是,您可以使用 (...).*
将单个记录表达式扩展到它的所有列——这也适用于触发器中的 NEW
和 OLD
记录变量,例如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
关键字传递。
在没有目标列列表的情况下使用 INSERT
(insert into some_table ...
而不是 insert into some_table (col1, col2, ...) ...
)是一件非常脆弱的事情。如果源和目标不匹配,插入很容易失败。 .
如果您不 运行 对审计表进行大量报告(其中具有明确的列名会更有效率),您可能需要考虑使用 JSON
或 HSTORE
列来存储整个记录。有几个现成的审计触发器可用: