前段时间在帮一个订单统计项目做SQL重构时,我遇到了一条很典型的查询:要把订单备注里的用户手机号脱敏、按支付月份分组、再把同一用户的多条订单号拼成一列。原本Java代码里绕了三层循环才做完的事情,最后用几条MySQL内置函数一条SQL就搞定了。那之后我就特别想把内置函数这件事系统讲清楚——因为很多人对它的认识停留在“需要时查一下”,但真正拉开SQL水平差距的,恰恰是对这些函数的边界、陷阱和执行行为的理解程度。
MySQL内置函数覆盖字符串、数值、日期时间、流程控制、聚合统计等场景,它不是一个“锦上添花”的知识点,而是把复杂业务压缩成一条清晰查询的核心武器。这篇内容适合刚学完基础SQL的初学者,也适合被慢查询、数据统计不准确折腾过的开发者。我不会把官方文档搬一遍,而是挑实际项目里真正影响结果和执行性能的部分,配合能直接复现的案例来讲。
1. 为什么说内置函数是SQL能力的“隐形天花板”
1.1 从ORDER BY和函数的关系说起
很多学习路径把MySQL内置函数放在“了解即可”的位置,但实际上,它是SQL表达能力的上限。举个例子,搜索热词里经常出现“mysql排序”,好像排序只是ORDER BY加个字段名的事。可一旦你遇到这种需求——按用户名的姓氏拼音排序、按订单创建时间的年月排序、按状态字段做自定义排序——你会发现这些全都离不开函数。
更典型的是函数和索引之间的关系。同一个字段,直接排序能命中索引,包一层函数之后可能就直接全表扫描。这不是函数本身写得不对,而是你对MySQL底层执行逻辑缺乏感知。所以我一直觉得,内置函数不是“语法点”,而是连接业务需求和数据库执行引擎的桥梁。
1.2 MySQL内置函数的分类全景
为了后面讨论方便,先把函数按作用域做一个整体梳理。MySQL内置函数常见分类如下:
| 类别 | 代表函数 | 典型业务场景 |
|---|---|---|
| 字符串函数 | CONCAT_WS、SUBSTRING_INDEX、REPLACE、GROUP_CONCAT | 数据清洗、脱敏、拼接 |
| 数值函数 | ROUND、CEIL、FLOOR、TRUNCATE、MOD | 金额处理、分页计算、取整 |
| 日期时间函数 | DATE_FORMAT、STR_TO_DATE、DATE_ADD、TIMESTAMPDIFF | 统计报表、时间范围判断 |
| 流程控制函数 | IF、CASE WHEN、IFNULL、COALESCE、NULLIF | 状态映射、空值兜底、条件聚合 |
| 聚合函数 | COUNT、SUM、AVG、MAX、MIN | 分组统计、明细汇总 |
| 加密/信息函数 | MD5、SHA2、USER、DATABASE | 安全存储、运维排查 |
有些函数在分类上有重叠,例如GROUP_CONCAT既是字符串函数也是聚合函数。这种交叉恰恰说明它在实际查询中很有分量。后面每一类我都会挑几个“看着简单但容易翻车”的用法展开讲,而不是把参数列表罗列一遍。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 字符串函数:数据清洗与展示的第一战场
2.1 SUBSTRING_INDEX、INSTR:截取和定位操作不要靠肉眼
SUBSTRING_INDEX可能是最被低估的字符串函数,它的语义是:在给定字符串中,按分隔符查找第N次出现的位置,并返回分隔符左侧或右侧的内容。用法是SUBSTRING_INDEX(str, delim, count),count为正数时从左往右数,取第count个分隔符左边的部分;count为负数时从右往左数,取第count个分隔符右边的部分。
我给你一个实战场景。一张订单表里存了邮箱,你想把@后面的域名取出来做用户来源分析,传统写法要配合LOCATE和SUBSTRING,写成SUBSTRING(email, LOCATE('@', email) + 1),逻辑绕且容易出错。用SUBSTRING_INDEX就一行:
sql复制SELECT
email,
SUBSTRING_INDEX(email, '@', -1) AS domain_name
FROM user_order;
再看一个更常见的拆分需求:IP地址“192.168.1.10”,想取最后一段:
sql复制SELECT SUBSTRING_INDEX('192.168.1.10', '.', -1) AS last_part;
-- 结果:10
这里有个容易忽略的关键点:当count为负数时,它是“从右往左数,取分隔符右边的部分”,结果切出来的还是原顺序的右侧内容。很多人第一次用会误以为结果会倒序,其实不是。
INSTR和LOCATE也很容易弄混。INSTR(str, substr)是从str里找substr第一次出现的位置,返回从1开始的下标;LOCATE(substr, str, pos)的参数顺序恰好反过来,还支持从指定位置开始找。写复杂SQL时经常需要“先定位再截取”,如果你把两个函数的参数顺序搞反,返回NULL还算好的,返回错误位置更麻烦,因为数据不会报错,只会悄悄变错。
2.2 CONCAT_WS与GROUP_CONCAT:拼接操作里的两个极端
CONCAT是入门级函数,但它有个早年最容易踩的坑:只要有一个参数是NULL,整个拼接结果就是NULL。比如订单表中部分用户没有填备注,你执行CONCAT(user_name, '-', remark),遇到备注为NULL的行,结果不是“张三-”,而是整个NULL。这在拼地址、拼名字时非常常见,老手通常直接用CONCAT_WS代替。
CONCAT_WS的第一个参数是分隔符,后面传多个字段,它会自动跳过NULL值:
sql复制SELECT
user_name,
CONCAT_WS('-', user_name, remark) AS user_remark
FROM user_order;
这个函数在很多老项目里还没被普及,但它非常适合处理“存在空值但不想影响整体拼接”的场景。注意一点:CONCAT_WS不会跳过空字符串,它只跳过NULL,如果数据里本身写了空字符串,你看到的拼接结果里仍会
