存储过程参数必须开头校验null并处理空串,使用nullif统一转空值,可选参数过滤用带括号的(@p is null or col = @p),字符串拼接优先用concat避免null中断。

参数开头就判 NULL,别等用到才炸
存储过程里参数一上来就是 NULL,但你没拦住,后面 CONVERT(INT, @id) 或 WHERE order_id = @id 就直接报错或全表扫描。必须在 BEGIN 后立刻处理:
- 每个必填参数单独写
IF @param IS NULL THROW 50000, '参数 @param 不能为空', 1; - 字符串类参数额外加空格校验:
IF @name IS NULL OR LEN(LTRIM(RTRIM(@name))) = 0 THROW 50000, '参数 @name 不能为空或纯空白', 1; - 别用
= NULL——它永远不成立;也别把校验塞进WHERE里,比如@id IS NOT NULL AND t.id = @id,这会让索引失效
可选参数过滤用 (@param IS NULL OR column = @param),括号不能少
这是最稳的写法,SQL Server 优化器能识别并保留索引。但括号漏一个,逻辑就崩了:
- 正确:
(@status IS NULL OR t.Status = @status) - 错误:
@status IS NULL OR t.Status = @status AND t.Deleted = 0—— 因为AND优先级高于OR,实际变成(@status IS NULL OR t.Status = @status AND t.Deleted = 0),不是你要的效果 - 字符串还要防空串:
(@keyword IS NULL OR @keyword = '' OR t.Title LIKE '%' + @keyword + '%') - 数值/日期一般只判
IS NULL,除非业务明确允许传0或'1900-01-01'当占位符
空字符串统一转 NULL,避免 everywhere 判空
前端传来 '' 或 ' ',你在每个条件里都补 OR @name = '',既啰嗦又易漏。不如开头一刀切:
-
SET @name = NULLIF(LTRIM(RTRIM(@name)), '');—— 把空格和空串全转成NULL - 之后所有过滤条件只需写
(@name IS NULL OR t.Name = @name),干净利落 - 如果要区分“用户真输了个空格”和“根本没填”,改用
DATALENGTH(@name) = 0,它比LEN()更严格(LEN(' ') = 0,但DATALENGTH(' ') = 1)
CONCAT 拼接更安全,+ 号遇到 NULL 就全完蛋
写 SELECT @msg = '订单' + CAST(@order_id AS VARCHAR) + '状态更新',只要 @order_id 是 NULL,整个 @msg 就变 NULL。换成 CONCAT:
-
SET @msg = CONCAT('订单', @order_id, '状态更新');——NULL自动当空串,结果是'订单状态更新' - 需要分隔符?用
CONCAT_WS(', ', @a, @b, @c),自动跳过NULL值,不用提前ISNULL包裹 - 注意:触发器或循环里
SELECT ... INTO @var查不到数据时,@var不会变NULL,得手动SET @var = NULL再查,否则值是上一次残留的
(@p IS NULL OR col = @p),SQL Server 在列选择性差或统计信息陈旧时,仍可能放弃索引走全表扫描——这时候得看执行计划,而不是只信语法。











