
[英]How can I insert the return of DELETE into INSERT in postgresql?
[英]Declare and return value for DELETE and INSERT
我正在尝试根据唯一 ID 从我们的某些数据库中删除重复数据。 所有删除的数据都应该存储在一个单独的表中以供审计。 由于它涉及相当多的数据库和不同的模式和表,我想开始使用变量来减少出错的机会和我需要的工作量。
这是我能想到的最好的示例查询,但它不起作用:
do $$
declare @source_schema varchar := 'my_source_schema';
declare @source_table varchar := 'my_source_table';
declare @target_table varchar := 'my_target_schema' || source_table || '_duplicates'; --target schema and appendix are always the same, source_table is a variable input.
declare @unique_keys varchar := ('1', '2', '3')
begin
select into @target_table
from @source_schema.@source_table
where id in (@unique_keys);
delete from @source_schema.@source_table where export_id in (@unique_keys);
end ;
$$;
查询语法适用于硬编码值。
大多数时候,我的变量被视为列或根本不被识别。 :(
您需要创建并使用输入参数调用 plpgsql 过程:
CREATE OR REPLACE PROCEDURE duplicates_suppress
(my_target_schema text, my_source_schema text, my_source_table text, unique_keys text[])
LANGUAGE plpgsql AS
$$
BEGIN
EXECUTE FORMAT(
'WITH list AS (INSERT INTO %1$I.%3$I_duplicates SELECT * FROM %2$I.%3$I WHERE array[id] <@ %4$L :: integer[] RETURNING id)
DELETE FROM %2$I.%3$I AS t USING list AS l WHERE t.id = l.id', my_target_schema, my_source_schema, my_source_table, unique_keys :: text) ;
END ;
$$ ;
过程duplicates_suppress
插入到my_target_schema.my_source_table || '_duplicates'
my_target_schema.my_source_table || '_duplicates'
my_source_schema.my_source_table
中 id 在数组unique_keys
中的行,然后从表my_source_schema.my_source_table
中删除这些行。
在dbfiddle中查看测试结果。
如前所述,您需要某种动态 SQL。 在FUNCTION
、 PROCEDURE
或DO
语句中在服务器上执行此操作。
您应该对PL/pgSQL感到满意。 动态 SQL 不是初学者的玩具。
带有PROCEDURE
的示例,就像 Edouard 已经建议的那样。 您将需要一个FUNCTION
来将其包装在外部事务中(就像您很可能一样)。 看:
CREATE OR REPLACE PROCEDURE pg_temp.f_archive_dupes(_source_schema text, _source_table text, _unique_keys int[], OUT _row_count int)
LANGUAGE plpgsql AS
$proc$
-- target schema and appendix are always the same, source_table is a variable input
DECLARE
_target_schema CONSTANT text := 's2'; -- hardcoded
_target_table text := _source_table || '_duplicates';
_sql text := format(
'WITH del AS (
DELETE FROM %I.%I
WHERE id = ANY($1)
RETURNING *
)
INSERT INTO %I.%I TABLE del', _source_schema, _source_table
, _target_schema, _target_table);
BEGIN
RAISE NOTICE '%', _sql; -- debug
EXECUTE _sql USING _unique_keys; -- execute
GET DIAGNOSTICS _row_count = ROW_COUNT;
END
$proc$;
称呼:
CALL pg_temp.f_archive_dupes('s1', 't1', '{1, 3}', 0);
db<> 在这里摆弄
我将程序设置为临时的,因为我认为您不需要永久保留它。 每个数据库创建一次。 看:
传递的模式和表名是区分大小写的字符串。 (与普通 SQL 中未加引号的标识符不同,)无论哪种方式。 动态连接 SQL 时要小心 SQL 注入:请参阅:
制作_unique_keys
类型int[]
( integer
的数组),因为您的样本值看起来像整数。 使用id
列的实际数据类型!
变量_sql
保存查询字符串,因此可以在实际执行之前轻松调试它。 使用RAISE NOTICE '%', _sql;
为了这个目的。
我建议在您确定之前评论EXECUTE
行。
我让PROCEDURE
返回已处理的行数。 您没有要求这样做,但它通常很方便。 几乎不惜任何代价。 看:
最后但同样重要的是,在数据修改 CTE中使用DELETE... RETURNING *
。 因为它只需要在单独的SELECT
和DELETE
成本的一半左右找到行。 而且它非常安全。 如果出现任何问题,无论如何都会回滚整个事务。
两个单独的命令也可能遇到并发问题或竞争条件,这些问题可以通过这种方式排除,因为DELETE
会隐式锁定要删除的行。 例子:
或者,您可以在客户端程序中构建语句。 像 psql,并使用\gexec
。 例子:
根据欧文的回答,小优化......
create or replace procedure pg_temp.p_archive_dump
(_source_schema text, _source_table text,
_unique_key int[],_target_schema text)
language plpgsql as
$$
declare
_row_count bigint;
_target_table text := '';
BEGIN
select quote_ident(_source_table) ||'_'|| array_to_string(_unique_key,'_') into _target_table from quote_ident(_source_table);
raise notice 'the deleted table records will store in %.%',_target_schema, _target_table;
execute format('create table %I.%I as select * from %I.%I limit 0',_target_schema, _target_table,_source_schema,_source_table );
execute format('with mm as ( delete from %I.%I where id = any (%L) returning * ) insert into %I.%I table mm'
,_source_schema,_source_table,_unique_key, _target_schema, _target_table);
GET DIAGNOSTICS _row_count = ROW_COUNT;
RAISE notice 'rows influenced, %',_row_count;
end
$$;
-- 如果你的 _unique_key 不是那么多,这个解决方案也会为你创建一个表。 显然,您需要自己创建目标模式。
如果你的 unique_key 太多,你可以自定义以正确重命名转储表。
让我们称呼它。
call pg_temp.p_archive_dump('s1','t1', '{1,2}','s2');
s1
是源模式, t1
是源表, {1,2}
是您要提取到新表的唯一键。 s2
是目标模式
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.