从 MySQL 迁过来的一段统计 SQL,放到 SQL Server 上直接报错,提示 IFNULL 不是可以识别的内置函数名称。查了半天才明白,这个名字在 SQL Server 里压根没有。
对应的是 ISNULL
SQL Server 用 ISNULL(表达式, 替换值),只接受两个参数,第一个为 NULL 时返回第二个。MySQL 的 IFNULL 语义一样,但可以层层嵌套;Oracle 叫 NVL;标准 SQL 里是 COALESCE,参数个数不限。迁移时把这几个名字对应上,大部分空值处理就能通。
SELECT ISNULL(remark, '') FROM orders;返回类型会被第一个参数带偏
ISNULL 的返回类型取第一个参数的类型。写成 ISNULL(CAST(NULL AS VARCHAR(10)), 'abcdefghijklmn'),结果是 10 个字符,后面两个字符直接没了。MySQL 的 IFNULL 按两个参数里更宽的类型走,行为不同,迁移后字段莫名被截断多半是这个原因。稳妥的做法是先把第一个参数转成足够长的类型再套函数。
多列兜底用 COALESCE
要依次取第一个非空值,写 COALESCE(mobile, phone, backup_phone, '未填写'),SQL Server 2008 之后都支持。它的返回类型按数据类型优先级决定,不是简单取第一个,遇到 int 和 varchar 混用会尝试隐式转换,转不了直接报错,混用之前先统一类型。
两个和索引有关的注意点
- 别在 WHERE 里包列:WHERE ISNULL(status, 0) = 1 会让 status 上的索引失效,改成 status = 1 OR status IS NULL。
- 聚合时不数 NULL:COUNT(字段) 会跳过空值,统计行数要用 COUNT(*),两个结果可能差很多。
- 排序里也别包:ORDER BY ISNULL(sort_no, 9999) 同样用不上索引,能在应用层补默认值就别在 SQL 里绕。
判断标准很直接:只在显示层做空值替换,放心用 ISNULL;一旦它出现在关联条件或过滤条件里,先看执行计划有没有退化成全表扫描。