Skip to content

字符串函数


在 MySQL 开发中,常常需要把多个字段或文字拼接成一个完整的字符串,比如展示用户完整姓名、地址,或者把多个业务信息合并输出。这时候 CONCAT 就派上用场了。

CONCAT(str1, str2, ...);
  • 功能:将多个字符串拼接成一个字符串

  • 特点:==如果其中任何一个参数为 NULL,整个结果会返回 NULL==

CONCAT_WS拼接参数时,自动跳过 NULL 值,只拼接非 NULL 的部分。

SELECT CONCAT('Hello', ' ', 'World') AS result;
-- 结果:Hello World

假设有一张房屋表,包含楼盘、楼栋、单元、房号:

SELECT CONCAT(fbv.villa_name, fbvb.building_name, fbvh.unit, fbvh.room_num) AS housingInfo
FROM house;
  • 结果示例:“绿谷小区A栋2单元301”

  • 场景:把分散的列整合成一个业务可读字段,便于展示或导出

  • 信息展示:姓名 + 工号,或者完整地址

  • 日志记录:把操作人、时间、动作拼成一条完整日志

  • 生成唯一标识:组合多个字段形成业务唯一 key

在日常开发中,经常需要 截取字符串的一部分。比如脱敏处理手机号、身份证号,或者只取名字的前几个字符。MySQL 提供了 LEFT 函数来方便地实现这一功能。

LEFT(str, length);
  • 功能:返回字符串左边的 length 个字符

  • 特点:

    • 如果 length 大于字符串长度,会返回整个字符串

    • 可用于数据脱敏、字段截取等场景

SELECT LEFT('abcdefg', 3) AS result;
-- 结果:abc

假设有一张申请人表,需要显示 姓名 + 身份证前 9 位 + 脱敏

SELECT CONCAT(bfm.name, LEFT(bfm.identity_number, 9), '****') AS applicantInfo
FROM applicant;
  • 结果示例:“张三123456789****”

  • 场景:保护用户隐私,只显示部分信息

  • 数据脱敏:身份证号、手机号、邮箱

  • 字段截取:名字、编号、编码前缀

  • 报告或展示:截取前几位生成简短标签

在地址、路径、标签等用分隔符拼接的场景里很常用;与 CONCAT 最大区别是:会自动忽略 NULL 参数

CONCAT_WS(separator, str1, str2, ...);
  • 功能:用指定 separator 把多个字符串连接起来

  • 特点:自动跳过 NULL;但不会跳过空字符串 ''

  • 对比:CONCAT 只拼接,无分隔符,且任一参数 NULL 会得到 NULL

SELECT
CONCAT_WS('',
bfm.residence_place_province,
bfm.residence_place_city,
bfm.residence_place_district,
bfm.residence_place_subdistrict,
bfm.residence_place_community,
bfm.residence_place_address
) AS `地区`
FROM bzdx_family_member bfm;
3.2.2. 用分隔符拼接成“省/市/区/…”样式
Section titled “3.2.2. 用分隔符拼接成“省/市/区/…”样式”
SELECT
CONCAT_WS('/',
bfm.residence_place_province,
bfm.residence_place_city,
bfm.residence_place_district,
bfm.residence_place_subdistrict,
bfm.residence_place_community,
bfm.residence_place_address
) AS `地区路径`
FROM bzdx_family_member bfm;
  • 地址/面包屑:省/市/区/街道/门牌

  • 标签/多值字段:用逗号或竖线连接

  • 日志/提示:只拼接存在的非空信息,避免多余分隔符

把字符串转为小写,便于规范化、去重或不区分大小写比较。

LOWER(str); -- 同义:LCASE(str)
  • 功能:返回小写版本

  • 特点:与字符集/排序规则有关;多数场景直接可用

SELECT LOWER(bfm.source_system) AS `数据来源`
FROM bzdx_family_member bfm;
  • 统一存储格式(如邮箱、系统标识)

  • 不区分大小写的查重/比较(也可结合 COLLATE

把字符串转为大写,常见于编码、编号统一。

UPPER(str); -- 同义:UCASE(str)
SELECT UPPER(bfm.source_system) AS `数据来源`
FROM bzdx_family_member bfm;
  • 统一编号/编码大小写

  • 展示层面强调可读性(如国家/区域代码)

左侧填充到指定长度;若目标长度小于原串,会发生左侧截断

LPAD(str, len, padstr);
  • 功能:将 strpadstr左侧补齐到 len 长度

  • 特点:若 len < str 长度,则返回左侧被截断后的字符串

LPAD/RPAD/SUBSTRINGlen字符语境下是按字符长度处理(常见 UTF-8 配置即可正确按字符计算)。

SELECT LPAD(bfm.name, 18, '*') AS `凭证`
FROM bzdx_family_member bfm;

说明:以上是“凑满 18 位”的展示技巧;若要脱敏(显示最后 N 位,其余用 * 左填充),可用:

SELECT LPAD(RIGHT(bfm.name, 1), CHAR_LENGTH(bfm.name), '*') AS `姓名脱敏`
FROM bzdx_family_member bfm;
  • 固定宽度展示(对齐输出、报表)

  • 掩码/占位效果(结合 LEFT/RIGHT

右侧填充到指定长度;若目标长度小于原串,会发生右侧截断

RPAD(str, len, padstr);

LPAD/RPAD/SUBSTRINGlen字符语境下是按字符长度处理(常见 UTF-8 配置即可正确按字符计算)。

SELECT RPAD(bfm.name, 18, '*') AS `凭证`
FROM bzdx_family_member bfm;

脱敏(保留首字,后面用 * 补齐到原长度):

SELECT CONCAT(LEFT(bfm.name, 1), RPAD('', GREATEST(CHAR_LENGTH(bfm.name)-1,0), '*')) AS `姓名脱敏`
FROM bzdx_family_member bfm;
  • 报表定宽列、打印对齐

  • 右侧补全占位符

用于去除空格或指定字符TRIM 更灵活,LTRIM/RTRIM 专注去左/右空格。

TRIM(str); -- 去两端空格
TRIM([BOTH | LEADING | TRAILING] remstr FROM str); -- 去两端/左/右 指定字符 remstr
LTRIM(str); -- 去左空格
RTRIM(str); -- 去右空格
SELECT TRIM(BOTH '-' FROM bfm.identity_number) AS `证件号`
FROM bzdx_family_member bfm;

去除证件号两边的-

  • 清洗脏数据(输入多余空格、分隔符)

  • LTRIM/RTRIM 只处理空格;如需去除特定字符,优先用 TRIM(... FROM ...)

从指定位置截取子串;支持可选长度以及负索引(从末尾倒数)。

SUBSTRING(str, pos); -- 从 pos 开始到结尾
SUBSTRING(str, pos, len); -- 从 pos 开始截取 len 个字符
-- 同义:SUBSTR(str, pos, len)
-- 说明:pos 从 1 开始;pos 为负数表示从末尾倒数的位置开始

LPAD/RPAD/SUBSTRINGlen字符语境下是按字符长度处理(常见 UTF-8 配置即可正确按字符计算)。

SELECT SUBSTRING(bfm.identity_number, -4) AS `证件尾号`
FROM bzdx_family_member bfm;
9.2.2. 组合脱敏(首字 + * * (长度-1))
Section titled “9.2.2. 组合脱敏(首字 + * * (长度-1))”
SELECT CONCAT(
SUBSTRING(bfm.name, 1, 1),
RPAD('', GREATEST(CHAR_LENGTH(bfm.name)-1,0), '*')
) AS `姓名脱敏`
FROM bzdx_family_member bfm;
  • 编码/编号分段提取(前缀、批次号)

  • 隐私脱敏(固定保留/隐藏规则)

用于按字符个数统计字符串长度。对包含中文、emoji 等多字节字符时尤为准确。

CHAR_LENGTH(str); -- 同义:CHARACTER_LENGTH(str)
  • 功能:返回字符串所含字符的个数

  • 特点:与字符集相关;多字节字符(如中文、emoji)计为 1 个字符

  • 返回:NULL 传入则返回 NULL

SELECT
CHAR_LENGTH('abc') AS c1, -- 3
CHAR_LENGTH('中文') AS c2, -- 2
CHAR_LENGTH('😊') AS c3; -- 1(emoji)
10.2.2. 校验证件号长度(按字符)
Section titled “10.2.2. 校验证件号长度(按字符)”
SELECT
bfm.identity_number,
CHAR_LENGTH(bfm.identity_number) AS len_char
FROM bzdx_family_member bfm
WHERE CHAR_LENGTH(bfm.identity_number) NOT IN (15, 18);
10.2.3. 脱敏保留首字符 + 其余用 *
Section titled “10.2.3. 脱敏保留首字符 + 其余用 *”
SELECT CONCAT(
SUBSTRING(bfm.name, 1, 1),
RPAD('', GREATEST(CHAR_LENGTH(bfm.name)-1, 0), '*')
) AS `姓名脱敏`
FROM bzdx_family_member bfm;
  • 长度校验:账号、证件号、手机号等(按“字符”而非字节)

  • UI 截断:限制标题/昵称长度,避免多字节字符被误判

  • 脱敏/切片:与 SUBSTRING/LPAD/RPAD 联合使用

用于按字节数统计字符串长度。适合与存储、编码、网络传输等“字节级”逻辑相关的场景。

LENGTH(str); -- 同义:OCTET_LENGTH(str)
  • 功能:返回字符串的字节长度

  • 特点:与字符集密切相关(如 utf8mb4 下:中文通常 3 字节,emoji 多为 4 字节)

  • 返回:NULL 传入则返回 NULL

11.2.1. 基础对比(以 utf8mb4 为例)
Section titled “11.2.1. 基础对比(以 utf8mb4 为例)”
SELECT
LENGTH('abc') AS b1, -- 3
LENGTH('中文') AS b2, -- 6(每个中文 3 字节)
LENGTH('😊') AS b3; -- 4(emoji 常为 4 字节)
11.2.2. 评估存储/报文体量(按字节)
Section titled “11.2.2. 评估存储/报文体量(按字节)”
SELECT
bfm.name,
LENGTH(bfm.name) AS bytes_utf8mb4
FROM bzdx_family_member bfm
ORDER BY LENGTH(bfm.name) DESC
LIMIT 10;

谨慎!按字节截断容易把多字节字符“截断坏”,除非你非常确定数据是单字节编码。

-- 示例:仅演示思路,不建议对多字节字符集直接这样做
SELECT SUBSTRING(bfm.remark, 1, 100) AS remark_truncated_bytes
FROM bzdx_family_member bfm;
  • 容量/配额:字段存储体积估算、导出大小控制

  • 接口/协议:按字节限制的报文、缓存键长度等

  • 兼容性检查:迁移到定长/字节敏感系统时的预检

12. CHAR_LENGTH & LENGTH 两者选择与注意事项

Section titled “12. CHAR_LENGTH & LENGTH 两者选择与注意事项”
  • 展示/校验/脱敏:用 CHAR_LENGTH(按字符更贴近“人类视角”)。

  • 存储/传输/配额:用 LENGTH(按字节更贴近“机器视角”)。

  • utf8mb4 下:大部分中文 ≈ 3 字节;常见 emoji ≈ 4 字节。

  • 不同字符集会影响 LENGTH 的结果,但不影响 CHAR_LENGTH 的“字符数”。

  • VARCHAR 中尾随空格是实际存储的,CHAR_LENGTH('a ')LENGTH('a ') 通常都算上空格。

  • CHAR(N) 类型,MySQL 在比较时会忽略尾随空格;如需“目视长度”,建议 RTRIM 后再计算:

SELECT CHAR_LENGTH(RTRIM(col)), LENGTH(RTRIM(col)) FROM t;
  • CHAR_LENGTH(NULL)LENGTH(NULL) 都返回 NULL

  • 若需把 NULL 当作空串参与计算,可用 IFNULL(col, '')

  • WHERE 中对列应用函数(如 WHERE CHAR_LENGTH(col)=…)通常会失去索引利用。

  • 可考虑:

    • 通过生成列GENERATED COLUMN)保存长度并建索引;

    • 或在写入时冗余存储长度字段;

    • 或把过滤条件改写为能利用索引的形式。