最常用、最安全的做法是为每个可选参数使用(@param is null or column = @param)形式,字符串需额外处理空字符串,数值/日期一般只判null;避免漏括号、隐式转换、动态拼接,优先参数化与索引优化。

WHERE 中用 OR 判断参数是否为空
最常用、最安全的做法是把每个可选参数的条件写成 (@param IS NULL OR column = @param) 形式。SQL Server 优化器能识别这种模式并生成合理执行计划,不需要动态拼 SQL。
常见错误是漏掉括号,比如写成 @param IS NULL OR column = @param AND ...,结果因 AND 优先级高导致逻辑错乱。
- 必须为每个可选条件加完整括号:
(@name IS NULL OR t.Name = @name) - 字符串类型还要考虑空字符串:
(@name IS NULL OR @name = '' OR t.Name = @name) - 数值或日期类型一般只需判
IS NULL,除非业务明确允许传入 0 或 '1900-01-01' 作占位符 - 如果参数来自前端,建议在存储过程开头统一处理:把空字符串转成
NULL,避免重复判断
用 LEN() 或 DATALENGTH() 判空字符串更准
LEN() 对尾部空格不敏感(会自动截断),DATALENGTH() 返回真实字节数,适合严格判空场景。比如用户可能输了个空格,LEN(' ') = 0,但 DATALENGTH(' ') = 1。
- 想忽略纯空格输入:
(@keyword IS NULL OR DATALENGTH(@keyword) = 0 OR t.Title LIKE '%' + @keyword + '%') - 若只关心语义空(含空格也算空),用
LEN(LTRIM(RTRIM(@keyword))) = 0 - 注意
LEN(NULL)返回NULL,所以仍要先判IS NULL,不能只靠LEN
多个可选参数组合时,别硬塞进一个 WHERE 块
8 个可选参数全堆在 WHERE 里,即使都加了 OR,SQL Server 也可能放弃索引走全表扫描——尤其当列选择性差或统计信息过期时。
- 高频过滤字段(如
Status、CreateTime)建议保留独立条件,不参与OR包裹 - 低频或高基数字段(如
Remark)才用(@remark IS NULL OR t.Remark = @remark) - 必要时可拆成 IF 分支:先查出主键列表,再用
IN关联详情,比单条大 WHERE 更可控 - 避免在
OR条件里混用不同数据类型比较(如@id = t.ID OR @name = t.Name),可能触发隐式转换拖慢性能
千万别用动态拼接来“图省事”
看到参数多就想 SET @sql = 'SELECT ... WHERE 1=1' 然后 IF @name IS NOT NULL SET @sql += ' AND Name = ''' + @name + '''' —— 这是 SQL 注入温床,也是性能黑洞。
- 哪怕加了
QUOTENAME(),每次执行都重新编译,无法复用执行计划 - 字符串拼接容易出引号、括号、空格错误,调试成本远高于写几个
OR条件 - 唯一可接受的动态场景:表名/列名/排序字段等标识符动态化,且必须配合
QUOTENAME()+ 白名单校验 - 所有用户输入值,一律走
sp_executesql参数化,绝不拼进字符串
真正难的不是写对语法,而是想清楚哪些参数该走索引、哪些该容忍全表扫描、哪些值该被归一化为 NULL。一个 OR 条件背后,连着统计信息、参数嗅探、执行计划缓存三座山。











