从 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;一旦它出现在关联条件或过滤条件里,先看执行计划有没有退化成全表扫描。