1. 函数认知与设计思路
1.1 为什么你绕不开 MySQL 函数
先说句实在话,只要是跟数据库打交道的人,不管你是刚入门的开发、做数据分析的,还是兼职维护数据库的运维,MySQL 函数这东西你迟早要正面碰上一次。很多时候你写 SQL 写得很痛苦,一个字段一个字段地挑,一个条件一个条件地凑,最后发现别人用几个函数就能把同样的事情解决了,而且查询速度还快得多。这不是你不够聪明,而是你没把函数当成 SQL 语句的一部分来理解。
MySQL 函数本质上就是 MySQL 服务器内置好的一组"加工工具",它们接收你传进去的参数,按既定规则处理,最后返回一个结果。你可以把它类比成做饭时的调味料——菜还是那个菜,但加了合适的调料之后,味道完全不一样。函数不改变表里的原始数据,只负责在查询结果里输出你想要的形态。这也就意味着,你可以放心地在一个 select 语句里对同一个字段做多种函数处理,完全不用担心理论上把数据弄脏。
做开发这些年,我见过太多人一碰到"取月份""算天数差""拼接字符串"这类需求,第一反应是先查出来,再用 Java 或者 Python 在程序里一个 for 循环慢慢处理。代码写了一大堆,性能还不行。其实这些事放在 SQL 这一层做,一条语句就能搞定,而且数据库服务器本来就擅长批量计算,效率比你在应用层循环高很多。学会函数之后,你会发现很多原本觉得很麻烦的查询需求,本质上就是"一个函数 + 一个参数"的问题。
1.2 函数在 SQL 执行流程里的位置
要真正用好函数,你得先搞清楚它到底在 SQL 的哪个环节发挥作用。MySQL 的 SQL 执行大致可以分成这么几步:客户端发送 SQL 语句,服务器做语法解析和权限校验,优化器生成执行计划,存储引擎检索数据,最后服务器层按查询要求做计算、排序、分组,然后返回结果。
函数主要在两个地方出现。第一种是在 where 条件里,用于过滤数据。这时候函数会对每一条待检查的记录进行计算,返回一个真值或假值,决定这条记录要不要留下来。第二种是在 select 列表里,用于对已经查出来的结果做投影处理,把所有记录的目标字段加工成最终展示形态。还有第三种是在 group by 或 order by 里,用于对分组或排序的依据做变换,这个稍微少见一些,但在处理日期、字符串排序时特别有用。
理解了这个位置关系,你就能明白一个重要的性能原则:在 where 里对索引字段使用函数,通常会直接导致索引失效,因为 MySQL 必须对每一行都先做一次函数运算,才能判断是否满足条件,原来的索引值已经被函数改头换面了,走不了索引快速定位。这并不是说 where 里坚决不能写函数,而是说你要清楚代价,在数据量大、查询频繁的场景下要想办法绕开这个限制,比如把条件改写为范围查询,或者使用生成的列来预先存储函数结果。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 常用函数分类详解与实操要点
2.1 字符串函数:你用得上但未必用得对的几个
字符串处理是日常开发中最频繁的需求了吧。我在网上看过不少帖子,发现很多人对 MySQL 字符串函数的记忆是混乱的,一会儿记成这个名,一会儿记成那个名,写出来报错了又到处查。说几个最常见的。
SUBSTRING() 函数用于截取子串,但很多人会记成 SUB_STR 或者 SUBSTR。这里明确说一下,MySQL 中正确的函数名是 SUBSTRING(),不过为了兼容性也提供了 SUBSTR() 作为同义别名。所以你在网上看到"sub_str 函数"这种写法,在 MySQL 里就是不存在的,MySQL 用的是 SUBSTRING,不叫 SUB_STR。截取时从 1 开始计数,不是从 0 开始,这也是新手最容易踩的坑。比如 SUBSTRING('hello', 2, 3) 返回的是 ell,不是 hel。
CONCAT() 函数用于拼接字符串,这个几乎没有任何难度,但有一个细节很多人不知道:如果任何一个参数为 NULL,整个结果就是 NULL。这个特性在很多业务场景里会带来意想不到的问题。比如你想把用户的姓和名拼起来,但中间名那一列是空的,结果整个名字都变 NULL 了。解决办法是用 CONCAT_WS(),第一个参数传分隔符,后边的参数里如果有 NULL,它会自动跳过,但并不影响其他非 NULL 部分正常拼接。比如 CONCAT_WS(' ', '张', NULL, '伟') 返回 张 伟,而不是直接给你一个 NULL。
还有一个很实用的函数 LEFT() 和 RIGHT(),分别从左边或右边截取指定字符数。在处理手机号脱敏、证件号后六位提取这些场景里特别顺手。LEFT('13812345678', 3) 返回 138,RIGHT('13812345678', 4) 返回 5678。
LENGTH() 和 CHAR_LENGTH() 这两个函数也经常有人搞混。LENGTH() 返回的是字节数,在 utf8mb4 编码下,一个汉字占 3 个字节;CHAR_LENGTH() 返回的是字符数,不管什么字符都按 1 计算。比如 LENGTH('你好') 返回 6,CHAR_LENGTH('你好') 返回 2。处理用户输入内容的长度校验时,用 CHAR_LENGTH() 更符合直觉。
REPLACE() 做字符串替换,能力很强,一个 SQL 就能把整张表里的某个子串替换掉。比如把产品描述里所有的 "iPhone" 统一替换成 "iPhone SE":UPDATE products SET description = REPLACE(description, 'iPhone', 'iPhone SE')。注意这个函数的替换是全局替换,也就是说一个字符串里只要有目标子串出现,它都会替掉,没法指定只替换第几次出现的那个。
2.2 日期时间函数:最常用但也最容易踩坑的类型
日期时间函数在报表统计里几乎是天天用。NOW() 返回当前日期和时间,CURDATE() 只返回当前日期,CURTIME() 只返回当前时间。这三个应该是最熟的了,但实际工作中我发现很多人不知道 NOW() 和 SYSDATE() 的区别。NOW() 返回的是语句开始执行的时间点,在整个 SQL 的执行期间都是同一个值;而 SYSDATE() 返回的是函数被调用那一刻的真实系统时间。在一个执行时间比较长的 UPDATE 语句里,如果你用 SYSDATE() 更新多个记录的更新时间字段,那这几条记录的时间戳可能是不同的。这个细节在一些需要精确追踪变更时间的系统里可能会引发问题,我的建议是默认用 NOW(),除非你有特殊理由。
日期提取也是高频操作。YEAR()、MONTH()、DAY() 分别提取日期的年、月、日部分。DATE_FORMAT() 用来格式化日期输出,比如 DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s') 可以输出标准的 "2024-05-20 14:30:25" 这种格式。这里的格式符很容易记错,%Y 是四位年份,%y 是两位年份,%m 是两位月份,%c 是不带前导零的月份,%H 是 24 小时制的小时,%h 是 12 小时制的小时,%i 是分钟,%s 是秒。这个函数在生成报表标题、日志文件名这些场景里非常实用。
日期差值计算是另一个高频需求。DATEDIFF(date1, date2) 返回两个日期相差的天数,结果是 date1 减 date2。如果你想算两个日期之间差了几个月,MySQL 也有 `TIMESTAM
