You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL循环存储过程报错:重复数据统计中quote_ident使用问题

问题描述

需要实现一个逻辑:遍历表mfr_incentives.public.t_jh_dup_check中存储的表名与对应主键,统计各表内主键重复的记录数,并将统计结果插入到mfr_incentives.public.t_jh_dup_check_final_output表中。编写的DO块运行报错,怀疑quote_ident()的使用方式有误。

原代码如下:

do $$
declare
object_names varchar;
keys varchar;
rec record;
begin 
    for rec in select object_name, key from mfr_incentives.public.t_jh_dup_check
        loop
        object_names = rec.object_name;
        keys = rec.key;
         execute 'insert into mfr_incentives.public.t_jh_dup_check_final_output
                    select * from 
                        (select ' || quote_ident(object_names) || ', ' || quote_ident(keys) || ', ' || ' count(*), current_date from 
                                ( select ' || keys || ', count(*)
                                  from ' || object_names ||
                                  ' group by ' || keys || ' having count(*) > 1
                                ) a
                         ) a';
        end loop;
end; 
$$;
错误原因分析
  1. quote_ident()误用:quote_ident()的作用是转义单个数据库标识符(如列名、表名),但原代码中将表名变量通过quote_ident()处理后当作列名使用,导致SQL语法错误——实际需要输出的是表名字符串,而非对应表的列。
  2. 标识符拼接风险:直接拼接主键列名到SQL语句中,若列名包含特殊字符(如空格、大写字母)会触发语法错误,同时存在SQL注入隐患。
  3. 冗余嵌套查询:原代码的多层嵌套查询完全不必要,增加了SQL的复杂度和维护难度。
  4. HTML转义字符错误:PL/pgSQL中无需使用HTML转义字符>,直接使用>即可。
修正后的DO块代码
do $$
declare
    rec record;
    sql_stmt text;
begin 
    for rec in select object_name, key from mfr_incentives.public.t_jh_dup_check loop
        -- 构建安全的动态SQL语句
        sql_stmt := format(
            'INSERT INTO mfr_incentives.public.t_jh_dup_check_final_output
             SELECT %L, %I, count(*), current_date
             FROM (
                 SELECT %I
                 FROM %I
                 GROUP BY %I
                 HAVING count(*) > 1
             ) AS dup_records',
            rec.object_name,  -- %L:将表名作为字符串常量插入结果
            rec.key,          -- %I:安全转义主键列名(作为输出列)
            rec.key,          -- %I:安全转义主键列名(用于查询)
            rec.object_name,  -- %I:安全转义目标表名
            rec.key           -- %I:安全转义主键列名(用于分组)
        );
        execute sql_stmt;
    end loop;
end; 
$$;
关键改进说明
  • 使用format()函数构建动态SQL:通过%L生成字符串常量(确保表名作为字符串输出),%I自动转义标识符(表名、列名),彻底解决转义错误和SQL注入风险。
  • 简化SQL结构:移除冗余的嵌套查询,让逻辑更直观易懂。
  • 修复语法细节:将>替换为原生的>,符合PL/pgSQL语法规范。
  • 优化变量使用:直接循环读取rec的属性,去掉冗余的中间变量,代码更简洁。

内容的提问来源于stack exchange,提问作者Jonathan Hodge

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 10:30:41