优先用 CONCAT,但必须提前处理 NULL;MySQL 中 + 是数值加法而非字符串拼接,误用会导致隐式转换错误(如 'a' + 'b' → 0);CONCAT_WS 可自动跳过 NULL 参数,更安全;跨库应统一用 COALESCE 兜底或选用兼容的 ||(MySQL 5.7+ 支持)。

直接说结论:优先用 CONCAT,但必须提前处理 NULL;在 MySQL 中误用 + 会触发隐式类型转换,结果不是拼接而是加法,极易出错。
MySQL 里 + 不是字符串拼接,是数值加法
很多刚从 Java 或 C# 转过来的人会下意识写 'a' + 'b',结果得到 0——因为 MySQL 尝试把两个字符串转成数字,失败就当 0 处理:'abc' + 'def' → 0 + 0 = 0;'12' + '34' → 12 + 34 = 46。
-
+只在 SQL Server、Access 中真正用于字符串拼接;在 MySQL、PostgreSQL、SQLite 中它本质是算术运算符 - 哪怕字段值看着像字符串(如
id是 INT 类型),name + ' suffix'也会先尝试把name转数字,失败则为0,再加后缀的 ASCII 值(比如' suffix'首字符空格 ASCII 是 32) - 真要兼容多库或避免歧义,别碰
+做字符串操作
CONCAT 的 NULL 行为因数据库而异,不能默认“安全”
CONCAT('a', NULL, 'b') 在 MySQL 5.7+ 返回 'ab',但在 PostgreSQL、SQL Server、Oracle 中一律返回 NULL。这不是 bug,是标准差异。
- 跨库迁移或写通用 DAO 层时,直接用
CONCAT(col1, col2)很可能让某环境查不到数据(因为整行变NULL) - 稳妥写法是统一兜底:
CONCAT(COALESCE(col1, ''), COALESCE(col2, '')),或用CONCAT_WS(它对NULL参数自动跳过,但分隔符本身为NULL则整个结果为NULL) -
CONCAT_WS('-', first_name, middle_name, last_name)比手写三个COALESCE更简洁,且天然容忍中间字段为NULL
替代方案:|| 运算符更接近“纯拼接”,但支持度有限
|| 是 ANSI SQL 标准的字符串连接符,在 PostgreSQL、Oracle、SQL Server 2012+ 原生支持;MySQL 从 5.7 开始才启用(之前 || 是逻辑 OR)。
- 它的
NULL行为和CONCAT一致:任一操作数为NULL,结果即NULL(MySQL 5.7+ 除外) - 优势是语法轻量:
first_name || ' ' || last_name比CONCAT(first_name, ' ', last_name)少打括号和逗号 - 如果项目锁定 PostgreSQL 或 Oracle,
||是首选;若需兼容 MySQL 5.6 或更低版本,必须回避
MyBatis 等 ORM 场景下,拼接逻辑尽量下沉到 SQL 层
有人习惯在 Java 层拼好字符串再传进 SQL,比如 "%"+keyword+"%",这会导致参数无法走预编译,失去防注入能力;也有人滥用 $ 符号拼接字段名,风险极高。
- 推荐用
CONCAT('%', #{keyword}, '%')或CONCAT_WS('', #{prefix}, #{keyword}, #{suffix}),既保持 SQL 可读性,又确保参数绑定 - 动态字段名(如按不同条件排序)才考虑
$,且必须白名单校验,绝不能直传用户输入 - 注意 MyBatis 对
CONCAT的解析无特殊处理,照常传入即可,无需额外 escape
最易被忽略的一点:字符串拼接不是性能瓶颈,但 NULL 导致的逻辑中断是高频故障源。别假设数据库行为一致,每次写 CONCAT 前,先确认你当前连的是哪个版本、哪种数据库。

















