动态sql应优先使用sp_executesql实现参数化以复用执行计划并防注入;对象名(表名/列名)须经quotename()处理,值必须用参数传递,拼接后需print或select输出验证逻辑正确性。

动态 SQL 执行前必须检查 EXEC 和 sp_executesql 的语义差异
直接拼字符串后用 EXEC 执行,看似简单,但参数无法安全传入、执行计划不复用、容易被注入。真正该用的是 sp_executesql——它支持参数化,SQL Server 能缓存执行计划,也避免引号嵌套灾难。
-
EXEC('SELECT * FROM ' + @table_name):危险,@table_name一旦含单引号或恶意片段就崩 -
sp_executesql N'SELECT * FROM ' + @table_name, N'@id INT', @id = 123:错!表名不能参数化,只能用字符串拼接;但 WHERE 条件里的值必须参数化 - 正确姿势是:表名/列名等对象名用白名单校验或
QUOTENAME()处理,WHERE/ORDER BY 中的值一律走sp_executesql参数
调试动态 SQL 时别只看“执行成功”,要先查生成的语句本身
很多问题不是语法错,而是拼出来的 SQL 逻辑不对——比如多了一个逗号、少了一个括号、AND 被写成 OR,或者时间范围反了。光看“命令已成功完成”没用。
- 把拼好的 SQL 字符串先赋给变量,用
PRINT @sql或SELECT @sql输出,复制到新窗口手动执行验证 - 注意:SQL Server Management Studio 默认截断
PRINT输出为 4000 字符,长语句要用SELECT CAST(@sql AS XML)避免截断 - 如果用了
sp_executesql,记得把参数也一起打印出来,比如SELECT @sql, @params, @param_values,不然光看 SQL 不知道实际代入了啥
拼接动态 SQL 时 QUOTENAME() 不是可选项,是必选项
用户输入的表名、列名、排序字段,哪怕看起来“很干净”,也不能裸拼。SQL Server 对标识符有严格规则(比如含空格、中划线、中文),不处理会直接报错:Incorrect syntax near '-'.
Psky企业网站是基于PHP+MYSQL开发的一款企业官网宣传网站,支持PC、wap浏览。 主要功能包括导航管理、轮播新闻、单位动态、通知通报、单位简介、后台管理等。运行环境下载安装PHPStudy(小皮)即可。系统采用PHP语言(版本:7.3.4nts)和MYSQL(版本:5.7.26)数据库进行开发,安装PHPStudy(小皮)集成环境进行测试通过。安装步骤:1、导入SQL文件到PHPmyad
-
QUOTENAME(@table_name)会自动加[ ]并转义内部的],比手写'[' + @table_name + ']'安全得多 - 不要对值内容用
QUOTENAME()——那是给对象名用的;值要用参数,而不是拼进字符串 - 如果允许用户选多个列,用
STRING_AGG(QUOTENAME(col), ', ')(SQL Server 2017+)或循环拼接,别忘了每个列都套一层
动态 SQL 在存储过程中性能差?先确认是否真用了执行计划缓存
有人说“动态 SQL 性能差”,其实是没分清场景:纯字符串拼接每次都是新语句,肯定不缓存;但用 sp_executesql 且参数类型一致、SQL 文本完全相同,就能复用计划——和静态 SQL 一样快。
- 检查是否命中缓存:查
sys.dm_exec_query_stats+sys.dm_exec_sql_text,看同一段动态 SQL 的execution_count是否递增 - 常见破环缓存的行为:在 SQL 字符串里硬编码不同时间戳、用
GETDATE()、拼入不同长度的字符串(比如'abc'vs'abcd')、参数类型不一致(@id INTvs@id BIGINT) - 如果必须拼时间范围,用参数代替,比如
N'WHERE created_at BETWEEN @start AND @end',而不是'WHERE created_at BETWEEN ''' + CONVERT(VARCHAR, @start) + ''' AND ...'
动态 SQL 最难的从来不是怎么写出来,而是怎么让拼出来的那条语句,在所有边界输入下都合法、安全、可预测。尤其当它混着用户可控字段、条件开关、分页参数一起上时,漏掉一个 QUOTENAME() 或参数类型不匹配,就可能在线上突然报错或查出错误数据。









