SQL Server动态分组必须用sp_executesql并严格校验:列名用QUOTENAME+INFORMATION_SCHEMA验证,聚合函数白名单过滤,多字段用STRING_AGG或FOR XML拼接,ORDER BY须显式添加且字段同样校验。
不能直接在 GROUP BY 后面写变量,比如 GROUP BY @col_name,SQL Server 解析阶段就报错“必须声明标量变量”——这不是执行时的问题,是语法层面不合法。
动态分组本质是生成新 SQL 字符串再执行,EXEC 只能拼字符串字面量,一旦列名来自用户输入或配置表,极易被注入或类型错乱。sp_executesql 支持参数化,但注意:它只对**值参数**安全,对**列名、表名、函数名**等结构部分仍需手动拼接,必须额外校验。
QUOTENAME(@col_name) 包裹,自动加方括号并转义特殊字符(如空格、中划线)INFORMATION_SCHEMA.COLUMNS 确认该列真实存在,避免拼出无效字段SUM、COUNT)不能参数化,只能白名单校验后拼进字符串,例如 IF @agg_func IN ('SUM', 'COUNT', 'AVG')...
用户传入的分组字段可能是单个(region),也可能是多个(region, status),手写逗号拼接容易漏空格、混入关键字或结尾多逗号。
#valid_cols,每行一条字段名STRING_AGG(QUOTENAME(col), ', ') 组装FOR XML PATH('') 拼接,但结果开头无逗号、结尾多逗号,得用 STUFF(..., 1, 2, '') 去掉前两个字符SELECT ' + @cols + ', COUNT(*) FROM orders GROUP BY ' + @cols
执行完 sp_executesql 返回结果集,发现顺序每次不同,甚至没按预期字段排——这不是动态 SQL 的缺陷,而是 SQL 标准规定:没有 ORDER BY 的查询,结果顺序无定义。
ORDER BY 子句,且排序字段也要走 QUOTENAME() 校验total_amount DESC 排,但分组字段只有 region),要确认该字段在 SELECT 列表中已出现或可被聚合引用真正麻烦的不是拼 SQL,而是字段合法性校验链:用户输入 → 系统视图验证 → QUOTENAME 转义 → 白名单过滤聚合函数 → 动态组装 → 执行。漏掉任意一环,轻则报错,重则拖垮整个库。