你刚开始学 MySQL 的时候,是不是经常在 SQL 里看到 NOW()、COUNT(*)、IFNULL() 这种写法,别人告诉你“这是函数”,好像听懂了,但真到自己写的时候又不知道选哪个。这篇我就以 MySQL 数据库基础里的函数为主线,把最常用的几类函数掰开揉碎讲一遍,不只是列语法,还会告诉你每个函数在真实业务里怎么用、有哪些容易踩的坑。
我会用同一张员工表贯穿全文,从字符串处理、数值计算、日期时间统计,到条件判断、聚合和行转列,再到自定义函数和常见报错,让你学完就能直接在本地库上跑起来。内容同样适合准备 MySQL 面试的人,后半部分我会把面试里高频出现的函数考点和易错场景做一次集中梳理。
1. 先搞懂 MySQL 函数是什么,再开始写 SQL
1.1 为什么函数是 MySQL 基础里最绕不开的一块
函数本质上是一段封装好的处理逻辑,它接收一个或多个输入值,经过内置规则计算后返回一个结果。你可以把它理解成一个加工车间:原材料进去,成品出来。MySQL 官方把函数分成字符串函数、数值函数、日期时间函数、流程控制函数、聚合函数等几大类,日常开发里几乎每条稍复杂一点的 SQL 都会用到其中一类。
举个例子,你要查用户表里每个人的姓名长度,不能靠肉眼数,这时可以用 CHAR_LENGTH(name);你要算订单表中每个月的销售额,需要把下单时间格式化成年月,这时可以用 DATE_FORMAT(order_time, '%Y-%m');你还要处理 NULL 值,避免页面上显示空白,这时离不开 IFNULL。这些操作全部依赖函数。
我见过很多刚入行的同事,SQL 能写基础的增删改查,但一到统计报表、数据处理就卡壳,核心原因不是不会写 SELECT,而是不熟悉函数能提供什么能力。函数掌握得越熟,你写 SQL 的速度越快,也能少绕很多弯路。面试的时候,函数相关的问题也几乎必考,尤其是 COUNT 系和 CASE WHEN,所以要重视这块基础。
1.2 准备一张表,后面案例都用它
为了不让你看得云里雾里,我提前建一张员工表,后续的函数案例基本都围绕它展开。这张表结构简单,但足够覆盖字符串、数值、日期和聚合统计等场景。
sql复制CREATE TABLE emp (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(50),
department VARCHAR(50),
hire_date DATE,
salary DECIMAL(10, 2)
);
INSERT INTO emp (emp_id, emp_name, department, hire_date, salary) VALUES
(1, '张三', '技术部', '2021-06-15', 18000.00),
(2, '李四', '技术部', '2020-03-01', 22000.00),
(3, '王五', '运营部', '2022-11-20', 12000.00),
(4, '赵六', '运营部', '2019-07-08', 15000.00),
(5, '孙七', '产品部', '2023-01-10', 16000.00),
(6, '周八', NULL, '2022-05-05', 10000.00);
这张表里有几个特点值得留意:姓名是中文,处理长度时要区分字符数和字节数;部门有空值,用于测试聚合函数处理 NULL 的行为;入职日期跨度大,方便做日期差计算。你可以在本地 MySQL 里先执行上面的建表和插入语句,后面我给出的每段 SQL 你都亲手跑一遍,比干看效果好太多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 字符串函数实操:拼接、截取、清洗全流程
2.1 字符串拼接:CONCAT 和 CONCAT_WS,别被 NULL 坑到
字符串函数里使用频率最高的应该就是拼接。MySQL 提供了 CONCAT 和 CONCAT_WS 两个函数,前者按顺序把所有参数拼成一个字符串,后者在拼接时自动插入分隔符。
sql复制SELECT CONCAT(emp_name, '在', IFNULL(department, '未知部门'), '工作') AS info FROM emp;
这条 SQL 会返回类似“张三在技术部工作”的文本。注意我在中间用了 IFNULL(department, '未知部门'),这是因为 CONCAT 有一个非常容易踩坑的特性:只要任何一个参数为 NULL,整个结果就是 NULL。员工的部门为空时,如果不处理,得到的结果直接是 NULL,而不是“张三在未知部门工作”。
CONCAT_WS 的全称是 Concatenate With Separator,第一个参数是分隔符,后面跟多个字段。它和 CONCAT 最大的区别是:CONCAT_WS 会自动跳过 NULL 值,不会因为某一个字段为空就让整个结果变 NULL。
sql复制SELECT CONCAT_WS('-', emp_id, emp_name, department) AS tag FROM emp;
比如周八的部门是 NULL,上面这段 SQL 依然会返回“6-周八”,分隔符和字段之间也不会出现多余的空隔符。所以当你要拼多个字段并希望忽略空值时,优先考虑 CONCAT_WS,没必要自己写一堆 IFNULL。
2.2 截取和长度:SUBSTRING、SUBSTR、LEFT、RIGHT、LENGTH、CHAR_LENGTH
截取字符串在数据清洗里非常常见。MySQL 里 SUBSTRING(str, pos) 或 SUBSTRING(str, pos, len) 都能截取子串,SUBSTR 是它的同义词,两者完全等价,你不用纠结选哪个。需要注意 MySQL 的字符位置是从 1 开始数的,不是像编程语言那样从 0 开始。
我经常用这个特性做姓名脱敏。比如页面上要显示“张**”,可以这么写:
sql复制SELECT CONCAT(SUBSTRING(emp_name, 1, 1), '**') AS masked_name FROM emp;
如果想取字符串最后几位,可以用 RIGHT(str, len),比如 RIGHT(emp_name, 1) 取姓名最后一个字。还有 LEFT(str, len),从左边开始取。这些函数在导出脱敏数据、截取编号、解析固定格式字符串时都很实用。
再说一个面试高频题:LENGTH 和 CHAR_LENGTH 到底有什么区别。LENGTH 返回的是字符串的字节长度,CHAR_LENGTH 返回的是字符长度。在 utf8mb4 编码下,一个汉字占 3 个字节,一个英文字符占 1 个字节,所以 SELECT LENGTH('abc'), CHAR_LENGTH('abc'), LENGTH('张三'), CHAR_LENGTH('张三') 的结果分别是 3、3、6、2。当你统计用户昵称长度、做字段长度校验时,一定要想清楚业务是按字符算还是按字节算,如果用错,校验规则会出现莫名其妙的问题。
2.3 清洗和转换:TRIM、REPLACE、LPAD、RPAD
导入数据时,字段里前后带空格是最让人头疼的问题之一。TRIM(str) 可以去掉字符串首尾的空格,LTRIM 只去左边,RTRIM 只去右边。注意 TRIM 默认只处理首尾,不会去掉字符串中间的空格。如果中间混入了空格,可以用 REPLACE(str, ' ', '') 把所有空格替换掉,但那会把所有空格都删除,使用时要想清楚业务是否需要保留字与字之间的空格。
REPLACE 不只是清洗空格,还能做内容替换。比如离职员工姓名需要全部替换成“已离职”,直接写:
sql复制UPDATE emp SET emp_name = REPLACE(emp_name, '张三', '张XX') WHERE emp_name = '张三';
虽然实际开发里这样直接改数据比较少见,但 REPLACE 在数据订正、屏蔽敏感词、格式化文本时真的很常用。
LPAD(str, len, padstr) 和 RPAD 则用来做补位,比如订单号统一为 8 位,不够前面补 0。有一个细节:如果补位后的总长度小于原字符串长度,MySQL 会从右侧截断原字符串,而不是从左保留。比如 LPAD('abc', 2, '0') 结果是 'ab',这一点很多人不知道,容易在生成流水号时得到意外结果。
说一个真实场景。运营要求员工工号必须显示为 6 位,不满 6 位前面补零,可以写成 LPAD(emp_id, 6, '0')。这个操作在导出报表时非常常见,熟悉了能省不少处理时间。
3. 数值计算与日期时间函数:报表统计最依赖的组合
3.1 数值函数:ROUND、CEIL、FLOOR、MOD 用在哪
数值计算类函数在工资核算、统计汇总、分页逻辑里无处不在。先看几个最常见的:
ROUND(num, d):四舍五入,保留 d 位小数。CEIL(num):向上取整,返回大于等于参数的最小整数。FLOOR(num):向下取整,返回小于等于参数的最大整数。MOD(num1, num2):取余数。
举一个工资场景。假如公司计算年终奖,规则是月薪乘以 1.2 后四舍五入到整数,SQL 可以写成:
sql复制SELECT emp_name, ROUND(salary * 1.2) AS bonus FROM emp;
ROUND 的第二个参数可以省略,省略时取整到个位。如果你想保留两位小数做金额展示,就写 ROUND(salary, 2)。
CEIL 和 FLOOR 更像业务的“向上/向下取整”逻辑。比如统计员工平均工资后,如果考核口径要求向上取整到元,那用 CEIL(AVG(salary));如果要求向下取整,用 FLOOR(AVG(salary))。这两个函数在分页计算总页数时也很有用:总记录数 101 条,每页 10 条,计算总页数会写 CEIL(101 / 10),得到 11 页。
MOD 最常见的用途是判断奇偶数、做分表逻辑、轮询分配。比如把员工按 ID 奇偶分成两组做活动,查询奇数 ID 的员工就可以用 WHERE MOD(emp_id, 2) = 1。这个函数在面试中经常作为基础题出现,重点是知道它和取余运算的对应关系。
3.2 类型转换和“int+5”的隐式转换陷阱
很多同学在搜“mysql 中 int+5”时,其实是在找 MySQL 里字段做数值运算的规则。INT 类型字段加 5 很简单,直接写:
sql复制SELECT emp_id, emp_id + 5 AS new_id FROM emp;
但如果字段本身是 VARCHAR 类型,里面存的却是数字,情况就复杂了。假设有张表的 code 字段是 VARCHAR,值为 '100',执行 code + 5,MySQL 会自动把字符串转换成数字参与运算,结果是 105。这种转换叫隐式类型转换,在开发时很方便,但也容易埋雷:如果字符串里混入了非数字字符,比如 '100a',MySQL 转换时会得到 0 或按前缀数字处理。更直接的问题是,如果使用 WHERE code = 100,MySQL 必须把字段的每一行都转成数字再比较,索引会失效,查询性能下降明显。
所以,判断一个字段到底该用 INT 还是 VARCHAR,不能只看当前存的值像不像数字,还要考虑它是否参与算术运算、是否参与排序、是否有前导零等。像手机号、工号这类不需要计算的编号,用字符串更合适;而工资、年龄、数量这类要参与统计的字段,应该用数值类型。如果你必须在 SQL 里做显式类型转换,可以用 CAST(expr AS SIGNED) 或 CONVERT(expr, SIGNED),这样代码语义更清晰,不容易被隐式转换坑到。
3.3 日期时间函数:NOW、DATE_FORMAT、DATEDIFF、TIMESTAMPDIFF
日期函数是报表统计的命根子,但很多初学者总是记不住函数名。先掌握最核心的四个。
NOW() 返回当前的日期和时间,格式是 YYYY-MM-DD HH:MM:SS。CURDATE() 只返回当前日期,CURTIME() 只返回当前时间。在插入记录时如果要记录操作时间,用 NOW() 是很自然的做法。
DATE_FORMAT(date, format) 负责把日期格式化成指定样子。报表里最常见的需求是“按月份统计入职人数”:
sql复制SELECT DATE_FORMAT(hire_date, '%Y-%m') AS hire_month, COUNT(*) AS cnt
FROM emp
GROUP BY hire_month
ORDER BY hire_month;
注意 GROUP BY 后面可以使用 SELECT 里的别名,这在 MySQL 中是允许的。DATE_FORMAT 的格式符很丰富,%Y 是四位年份,%y 是两位年份,%m 是两位月份,%d 是两位日,%H 是 24 小时制小时,%i 是分钟,%s 是秒。写复杂报表前先记住这几个,剩下用到再查。
DATEDIFF(date1, date2) 返回两个日期相差的天数,计算方式是 date1 - date2。比如查询每位员工入职至今的天数:
sql复制SELECT emp_name, DATEDIFF(CURDATE(), hire_date) AS days_worked FROM emp;
如果还需要更精确的年月差,可以用 TIMESTAMPDIFF(unit, start, end)。这个函数支持 YEAR、MONTH、DAY、HOUR、MINUTE、SECOND 等单位。计算员工司龄最方便的方式是:
sql复制SELECT emp_name, TIMESTAMPDIFF(YEAR, hire_date, CURDATE()) AS work_years FROM emp;
TIMESTAMPDIFF 的单位参数要放在最前面,这个顺序和 DATEDIFF 不太一样,我刚接触时经常写反。日期函数里类似的易错点还有 DATE_ADD 和 DATE_SUB,它们用于在日期上增加或减少一段时间,比如统计近 30 天数据可以用 WHERE create_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)。注意 INTERVAL 关键字不能漏,漏了直接报语法错误。
4. 流程控制与聚合函数:从单行计算到多行统计
4.1 IF、IFNULL、NULLIF、CASE WHEN 怎么选
流程控制函数让 SQL 具备“判断”能力。MySQL 里最常用的是 IF(expr, v1, v2),如果 expr 为真返回 v1,否则返回 v2。比如根据工资判断员工等级:
sql复制SELECT emp_name,
IF(salary >= 15000, '高薪', '普通') AS salary_level
FROM emp;
IFNULL(v1, v2) 专门处理 NULL:如果 v1 是 NULL 就返回 v2,否则返回 v1。查询员工部门时,想避免显示 NULL,就直接用 IFNULL(department, '未分配')。
NULLIF(v1, v2) 的逻辑则相反,当两个值相等时返回 NULL,不相等时返回第一个值。这个函数很适合做除法防错。比如计算工资占比时,如果某个部门总工资为 0,直接用除法会报错或得到 NULL,但你可以先构造保护逻辑。不过真实业务里更稳妥的方式是配合 CASE WHEN。
CASE WHEN 是流程控制里最灵活、最值得你花时间掌握的一个。它支持多分支判断,可以根据多个条件返回不同结果。上面的工资等级判断也可以写成:
sql复制SELECT emp_name,
CASE
WHEN salary >= 20000 THEN '高薪'
WHEN salary >= 12000 THEN '中等'
ELSE '待提升'
END AS salary_level
FROM emp;
WHEN 子句会从上往下依次判断,谁先满足就返回谁的结果,所以在写区间条件时,顺序很重要。如果你把 salary >= 12000 放在前面,那工资 22000 的员工也会被判成“中等”,因为第一个条件已经命中了。这是 CASE WHEN 最常见的错误,我几乎每年都能在别人写的 SQL 里看到类似问题。
4.2 COUNT、SUM、AVG 与 NULL 的相爱相杀
聚合函数的共同特点是,它们把多行数据的计算结果汇总成一个值,所以经常和 GROUP BY 搭配使用。先区分几个容易混淆的写法:COUNT(*) 统计的是结果集的行数;COUNT(1) 的效果和 COUNT(*) 基本相同;COUNT(字段) 只统计该字段非 NULL 的行数。
这个差别在职级统计里体现得很明显。如果统计员工总数,用 COUNT(*);如果统计有部门的员工数,用 COUNT(department)。因为周八的部门是 NULL,他就会从第二个统计里被排除掉。很多面试官喜欢问 COUNT(*) 和 COUNT(字段) 的区别,答案核心就是是否忽略 NULL。
SUM(字段) 也会忽略 NULL 值。如果一个字段全为 NULL,SUM 返回 NULL 而不是 0。这里有一个容易出问题的场景:在报表里直接展示 SUM(salary),当没有任何数据匹配时你去查询,页面上可能会显示空白,因为返回的是 NULL。更稳妥的做法是用 IFNULL(SUM(salary), 0),把 NULL 转成 0 再展示。
AVG(字段) 同样是只对非 NULL 值求平均,它的分母是非 NULL 行数,不是总行数。这一点比 SUM 更隐蔽。假设统计部门平均工资,部门里有一个人工资是 NULL,AVG 会忽略这个人,只用其他人的工资计算平均值。如果你希望把 NULL 当作 0 参与平均,需要先 IFNULL 转一次。这类细节在数据分析里影响很大,处理前一定要明确业务口径。
4.3 GROUP_CONCAT 实现“行转列”的经典案例
每次有人搜“mysql 行转列”,最终都会遇到 GROUP_CONCAT。它能把分组内多行的某个字段拼接成一个字符串,以逗号分隔。比如我想看每个部门有哪些员工,一行一个部门展示,就可以这样写:
sql复制SELECT department,
GROUP_CONCAT(emp_name ORDER BY emp_id SEPARATOR '、') AS emp_names
FROM emp
WHERE department IS NOT NULL
GROUP BY department;
返回的结果就像一张二维表,部门作为行,员工列表作为一列,非常直观。这里的 ORDER BY emp_id 是 GROUP_CONCAT 内部排序,SEPARATOR 用来指定分隔符,默认是逗号。
GROUP_CONCAT 在一些场景下可以作为“伪行转列”的利器,但它也有一个限制:默认最大长度是 1024 字节。如果拼接内容很长,会出现结果被截断的情况。遇到这种问题,可以修改会话变量:
sql复制SET SESSION group_concat_max_len = 102400;
顺便说一句,严格的“行转列”通常是用 CASE WHEN 配合聚合函数实现的。比如统计各部门中高工资人数和低工资人数,可以这样构造成多列结果:
sql复制SELECT
SUM(CASE WHEN salary >= 15000 THEN 1 ELSE 0 END) AS high_salary_cnt,
SUM(CASE WHEN salary < 15000 THEN 1 ELSE 0 END) AS low_salary_cnt
FROM emp;
这本质上是把 CASE WHEN 的结果作为聚合函数的输入,再按条件做分组计数。面试时提到行转列,你如果能同时说出 CASE WHEN 和 GROUP_CONCAT 两种思路,会比只能背一段 SQL 好很多。
5. 自定义函数与“命令找不到”式报错的底层逻辑
5.1 内置函数与自定义函数的边界
MySQL 已经内置了几百个函数,覆盖绝大多数场景,但你总会遇到内置函数无法直接满足业务规则的时刻,这时可以创建自定义函数。所谓自定义函数,就是用 SQL 逻辑封装一段可复用的计算过程,创建完毕后,就可以像使用 NOW()、IFNULL() 一样在 SQL 里调用。
举个例子,公司计算工资时,规定社保基数上限是 20000,如果员工工资超过 20000,只按 20000 计算社保基数。这个逻辑你可以每次都写 CASE WHEN,但更优雅的方式是创建一个 calc_social_base(salary) 函数:
sql复制DELIMITER //
CREATE FUNCTION calc_social_base(salary DECIMAL(10,2))
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
IF salary > 20000 THEN
RETURN 20000;
ELSE
RETURN salary;
END IF;
END //
DELIMITER ;
创建完成后,查询语句就可以写成:
sql复制SELECT emp_name, salary, calc_social_base(salary) AS social_base FROM emp;
这样写的最大价值是逻辑复用。多个报表页面都要用到同一套计算规则时,改函数只需要改一处,而不是到处找 SQL 复制粘贴。不过要注意,自定义函数适合固定、明确、无副作用的计算规则,不适合做复杂的多表查询和数据修改。复杂的业务规则放到应用层或存储过程中会更可控。
5.2 函数调用报错:分清是数据库不存在,还是环境问题
网上经常看到一类报错:“claude 无法将‘claude’项识别为 cmdlet、函数、脚本文件或可运行程序的名称”,还有 git、pnpm、npm 的类似报错。很多新手一开始以为这是 MySQL 函数的问题,其实不是,这是系统的命令行找不到对应的可执行程序或命令。
我拿数据库函数做类比:你要使用一个函数,前提是这个函数真的存在于当前数据库中。如果你直接执行 SELECT my_func(1),而库里根本没定义 my_func,MySQL 会报 FUNCTION my_func does not exist。命令行报“无法识别为函数”也是同一个道理,系统在 PATH 环境变量里找不到对应的程序,所以不认识这个“命令”。这一类报错的排查思路其实很一致:先确认名称拼写是否正确、再确认对象是否真实存在、还要检查环境变量是否配置完整。
出现这种情况时,技术人要养成的习惯是看报错类别。如果来自 MySQL 客户端,一般是 SQL 语法或对象缺失;如果来自 PowerShell 或 CMD,那就是系统命令问题。两者不在一个层面,排查方向完全不同。很多人因为没分清这个边界,对着数据库折腾半天,最后发现是系统环境没装好,这是我见过最多的浪费时间方式之一。
5.3 写一个自定义函数并注意副作用
自定义函数虽然好用,但有几个限制必须提前知道。
第一,函数里不能执行会修改数据的 SQL,比如 INSERT、UPDATE、DELETE。如果你试图在函数里做这些操作,MySQL 会直接报错。这是因为函数设计初衷是“给定输入计算输出”,为了保证在查询语句中安全调用,不允许产生副作用。
第二,函数必须有返回值,RETURNS 后面要指定返回类型。常见类型包括 INT、VARCHAR、DECIMAL、DATE 等。
第三,创建函数前要确认二进制日志相关参数。如果 log_bin_trust_function_creators 为 0,并且你没有声明 DETERMINISTIC,创建过程可能被拒绝。上面的示例里我写了 DETERMINISTIC,它表示相同输入一定得到相同输出,这样数据库才允许函数在主从复制环境中被安全使用,因为每台机器算出的结果是一致的。
我还想提一个实际经验:自定义函数写完后,一定要做边界测试。比如输入负数、超大数、NULL,都要确认结果是否符合预期。在刚才的社保基数函数里,如果传入 NULL,函数返回的是 NULL,这在统计时会造成影响。如果你想避免 NULL 入参,可以在函数开头加一句判断:
sql复制IF salary IS NULL THEN
RETURN 0;
END IF;
这些细节别看小,生产环境里一旦被忽略,就可能出现个别员工社保基数异常的问题。
6. 常见问题排查与面试避坑经验
6.1 面试官爱问的6个函数相关陷阱
结合我的面试经历和帮人改简历的经验,MySQL 函数相关的高频考点集中在几个细节里。第一个是 COUNT(*)、COUNT(1) 和 COUNT(字段) 的区别,这个前面已经讲透,面试时要能脱口而出:前两者统计行数,后者忽略 NULL。第二个是 LENGTH 和 CHAR_LENGTH 的区别,核心是字节数和字符数。第三个是 CONCAT 遇到 NULL 返回 NULL,而 CONCAT_WS 会跳过 NULL。第四个是 IFNULL 和 COALESCE 的差异,前者只能接两个参数,后者可以接多个参数并返回第一个非 NULL 值。第五个是 CASE WHEN 分支顺序对结果的影响。第六个是 DATEDIFF 与 TIMESTAMPDIFF 参数顺序差异。
我把这些易错点整理成了下面这张速查表,方便你面试前快速过一遍。
| 知识点 | 关键结论 | 常见坑 |
|---|---|---|
| COUNT(*) vs COUNT(字段) | 字段版忽略 NULL 行 | 统计总数时容易少人数 |
| LENGTH vs CHAR_LENGTH | LENGTH 按字节,CHAR_LENGTH 按字符 | 中文统计结果差 3 倍 |
| CONCAT vs CONCAT_WS | CONCAT 遇 NULL 返回 NULL;CONCAT_WS 跳过 NULL | 拼接员工信息时结果变 NULL |
| IFNULL vs COALESCE | COALESCE 可多个参数 | 误以为 IFNULL 只能处理一个值 |
| CASE WHEN 顺序 | 从上到下先匹配先生效 | 区间顺序颠倒导致统计错乱 |
| DATEDIFF vs TIMESTAMPDIFF | DATEDIFF 结果是天数;TIMESTAMPDIFF 可指定单位 | 参数顺序写反 |
除了这些考点,面试官还喜欢让你手写一个“统计每个部门工资大于平均工资的人数”类 SQL。这种题目表面上考聚合,实际上考验你对子查询和函数组合的理解。遇到别慌,先写内层平均工资,再写外层条件计数。
6.2 平时我在工作中积累的几个小习惯
关于 MySQL 函数的实际使用,我在项目里总结了一些小习惯,不一定能直接在书里找到,但真的能帮你少加班。
第一,SELECT 里使用函数后,一定要给结果起别名。例如 DATE_FORMAT(hire_date, '%Y-%m') AS hire_month,起别名后无论是程序里取字段还是后续排序,都会方便得多。如果不写别名,不同数据库驱动返回的列名可能非常奇怪,接数据时容易踩坑。
第二,不要在 WHERE 子句里对索引字段套函数,比如 WHERE DATE_FORMAT(hire_date, '%Y-%m') = '2021-06'。这样写看起来没问题,但会让索引失效,导致全表扫描。更好的做法是写成范围查询:WHERE hire_date >= '2021-06-01' AND hire_date < '2021-07-01'。函数不是不能用,而是要用对地方。
第三,聚合统计前先确认 NULL 口径。是所有行都参与统计,还是只有非空值参与?业务方说“平均工资”时,如果你直接用 AVG(salary),而表中存在 NULL 工资记录,结果可能比预期偏高,因为 NULL 被忽略了。发现这类问题后,我通常会写一个取数说明文档,把 NULL 如何处理、统计范围是什么写清楚,这样和业务方沟通时能减少很多扯皮。
第四,刚开始学函数阶段,不要追求一次写出复杂公式。先把一条 SQL 拆成几段分别验证,比如先看原始数据,再看单函数效果,最后组合成完整逻辑。组合函数时尽量分步去写 SELECT,确认每一步结果正确后再嵌套。这样可以避免写出一个几百字的长 SQL,报错了却不知道错在哪儿。
对于工作里的常见报错,我也做了一份简明的排查表,供你参考:
| 报错场景 | 可能原因 | 快速排查方式 |
|---|---|---|
| FUNCTION xxx does not exist | 函数不存在或拼写错误 | 用 SHOW FUNCTION STATUS 查看 |
| 命令行无法识别命令 | 系统未安装该程序或未配置环境变量 | 检查 PATH,确认程序是否安装 |
| 数据被截断 | 字段长度不够或 GROUP_CONCAT 超限 |
调整字段长度或修改 group_concat_max_len |
| 日期函数结果不对 | 参数顺序错误或格式符写错 | 对照官方文档检查 %Y、%m 等 |
| SUM 结果空白 | 匹配不到数据时返回 NULL | 外层包 IFNULL(SUM(x), 0) |
如果你刚开始学 MySQL,我建议不要急着背函数大全,而是先建一张简单的表,然后把今天提到的这些函数挨个跑一遍,每跑一个都问自己一个问题:这个函数如果遇到 NULL 会怎样,如果参数顺序写反会怎样,如果用在实际报表里能解决什么问题。函数这个东西,光看是看不会的,真正坐在电脑前把数据查出来、看结果、弄明白每个细节,才是掌握得最快的路径。
