CONCAT() 遇任意参数为 NULL 则结果为 NULL,UPDATE 时若未防护会将原值覆盖为 NULL;正确做法是用 IFNULL(CONCAT(...), '') 包裹整个表达式,而非仅包裹单个字段。

直接说结论:CONCAT() 遇到任意一个参数为 NULL,整个结果就是 NULL;UPDATE 时若没做防护,就会把原字段值覆盖成 NULL,而不是保留旧值或拼接出空字符串。
CONCAT(NULL, 'abc') 为什么等于 NULL 而不是 'abc'?
MySQL 的 CONCAT() 是严格类型处理函数,它不自动跳过 NULL,也不做隐式转换。只要有一个参数是 NULL,整条表达式求值即为 NULL——这和 + 运算符在 SQL Server 中的行为类似,但比大多数开发者直觉中“拼接就该忽略空”的预期更硬。
- 常见错误现象:
UPDATE users SET full_name = CONCAT(first_name, ' ', last_name),一旦某行first_name或last_name为NULL,full_name就被设成NULL,原值丢失 - 注意:这不是“没更新”,而是明确写入了
NULL,ROW_COUNT()仍返回 1 -
CONCAT_WS(' ', first_name, last_name)同样受此规则影响,分隔符不会改变NULL传播逻辑
UPDATE 中用 IFNULL/COALESCE 包裹 CONCAT 的正确位置
必须把 IFNULL() 或 COALESCE() 套在整个 CONCAT() 表达式外层,不能只包单个字段——否则无法拦截 CONCAT() 自身的 NULL 返回。
- ❌ 错误写法:
CONCAT(IFNULL(first_name, ''), ' ', IFNULL(last_name, ''))—— 看似安全,但如果first_name和last_name都为NULL,结果是' '(一个空格),不是你想要的空字符串 - ✅ 正确写法:
IFNULL(CONCAT(first_name, ' ', last_name), '')—— 先拼,再兜底 - 更健壮的写法:
COALESCE(CONCAT(NULLIF(first_name, ''), IFNULL(CONCAT(' ', last_name), '')), first_name, last_name, ''),但通常没必要这么复杂
LEFT JOIN 场景下拼接字段更容易踩空值坑
跨表 UPDATE 时,LEFT JOIN 让右表字段天然可能为 NULL,而 CONCAT() 不会帮你“智能跳过”。例如:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
UPDATE orders o LEFT JOIN customers c ON o.customer_id = c.id SET o.shipping_label = CONCAT(c.name, ' / ', c.phone)
只要某订单没匹配到客户,c.name 和 c.phone 都是 NULL,CONCAT() 返回 NULL,shipping_label 就被清空。
- 务必配合
IFNULL():CONCAT(IFNULL(c.name, ''), ' / ', IFNULL(c.phone, '')) - 或者更清晰地控制空值行为:
CONCAT(COALESCE(c.name, '[未知]'), ' / ', COALESCE(c.phone, '-')) - 别依赖
WHERE c.id IS NOT NULL来规避——那会退化成INNER JOIN,失去左表全量更新意图
真正容易被忽略的是:这种空值覆盖在日志里完全静默,UPDATE 成功返回,ROW_COUNT() 非零,但业务字段已损坏。上线前一定要用 SELECT 模拟验证,重点查 CONCAT() 表达式是否在 NULL 输入下返回了预期值。










