CONCAT 是什么
CONCAT 把多个字符串拼成一个字符串,是最常用的字符串函数之一。MySQL 的 CONCAT 只要有一个参数为 NULL,整个结果就是 NULL,这与很多人的直觉相反;需要忽略 NULL 时用 CONCAT_WS,它用第一个参数作分隔符并跳过后续的 NULL 参数。PostgreSQL 的 concat() 会忽略 NULL 参数,而 || 运算符遇到 NULL 返回 NULL,函数与运算符的行为不一致,迁移 SQL 时要逐个核对。
基本信息
| MySQL 基本拼接 | CONCAT(a, b, c),任一参数为 NULL 则结果为 NULL |
|---|---|
| MySQL 带分隔符 | CONCAT_WS(sep, a, b, c),跳过 NULL 参数;sep 为 NULL 时结果为 NULL |
| PostgreSQL 函数 | concat(a, b) 忽略 NULL 参数 |
| PostgreSQL 运算符 | a || b 遇到 NULL 返回 NULL |
| MySQL 的 || | 默认是逻辑或,只有开启 PIPES_AS_CONCAT 才是拼接 |
| 非字符串参数 | 数字与日期会自动转换为字符串再拼接 |
典型使用场景
- 拼完整地址:省加市加区加详细地址,缺失部分用 COALESCE 兜底
- 生成展示名:姓加名、商品标题加规格
- 拼业务编号:给流水号加固定前缀,如 ORDER-20240501-0001
- 拼日志或对账文本:把多字段拼成一行便于比对
- 补全固定格式:给手机号或证件号加掩码片段
- 拼动态 SQL 片段:生成表名或测试数据(注意不要拼用户输入)
示例
示例
-- MySQL
SELECT CONCAT(province, city, district) AS addr FROM users;
SELECT CONCAT_WS('-', 'ORDER', '20240501', '0001') AS order_no; # ORDER-20240501-0001
-- 任一参数为 NULL,CONCAT 整体为 NULL
SELECT CONCAT('name: ', nickname) FROM users; # nickname 为 NULL 时整列为 NULL
SELECT CONCAT('name: ', COALESCE(nickname, '')) FROM users; # 兜底后不再变 NULL
-- PostgreSQL
SELECT concat('name: ', nickname); # 忽略 NULL
SELECT 'name: ' || COALESCE(nickname, ''); # 用 || 要自己兜底NULL 行为对照
| 写法 | MySQL | PostgreSQL |
|---|---|---|
| CONCAT(a, NULL) | 返回 NULL | concat 忽略 NULL 返回 a |
| CONCAT_WS('-', a, NULL) | 跳过 NULL 参数 | concat_ws 行为一致 |
| a || b 遇 NULL | 默认按逻辑或处理 | 返回 NULL |
| concat_ws(NULL, a, b) | 返回 NULL | 返回 NULL |
拼出来的是新值,不修改原列:想落库要配合 UPDATE … SET col = CONCAT(…),并先确认目标列长度够。
目标列是定长 CHAR 时,拼接结果可能被截断或补空格;长度不确定时用 VARCHAR 或 TEXT,并让数据库不启严格模式外的静默截断。
不要把用户输入直接拼进 SQL 文本再执行,这是注入的入口;需要动态标识符时必须用白名单或引号函数处理。
MySQL 里 GROUP_CONCAT 是分组内拼接,PostgreSQL 里对应 string_agg(col, sep),二者都受最大长度参数限制。
拼接实践要点
- 拼接前统一兜底:对可能为 NULL 的列先用 COALESCE 换成空串
- 留意结果长度:拼接后长度是各段之和,超出目标列长度会被截断
- 分隔符统一用 CONCAT_WS:它跳过 NULL,不会出现连续两个分隔符
- 分组内拼接要指定顺序:GROUP_CONCAT 内部补 ORDER BY,否则拼接顺序不确定
- 展示类拼接可放到应用层:跨库可移植性与格式灵活性更好,SQL 里只取原始字段
- 拼接串进日志前先脱敏:日志里的拼接结果常是敏感信息的泄露点
MySQL 的 GROUP_CONCAT 受 group_concat_max_len 控制,默认 1024 字节,拼接长文本前要先调大,否则结果被静默截断。
数字与日期参与拼接时按会话格式转换,不同环境结果可能不同;格式要求严格时先用 DATE_FORMAT 或 to_char 转成字符串再拼。
拼接与迁移要点
- 迁移 SQL 时逐条核对 NULL 行为:MySQL 的 CONCAT 与 PostgreSQL 的 concat 并不等价
- 拼接结果要写回表时先估算长度:超长在严格模式报错、非严格模式静默截断
- 拼接进 SQL 语句要转义:宁可用参数占位符,也不要手工拼字符串
- 大批量拼接注意内存:应用层拼接更灵活,也更容易分批处理
- 格式模板固定时交给应用层模板引擎:SQL 只取原始字段,展示格式变更不必改 SQL
MySQL 里最外层再套一次 COALESCE 是常见兜底:COALESCE(CONCAT(province, city), '') 可以保证前端拿到的是空串而不是空值。
拼接不同字符集的列时数据库可能要做隐式转换,既慢又可能出现乱码;跨库或跨字符集拼接前先确认两侧列的字符集一致。