☰
MySQL常用函数实战指南:从基础查询到数据处理,SQL效率倍增
2026/10/5 7:39:59 网站建设 项目流程

零基础学MySQL,每个新手都会经历这样一个阶段:表建好了,数据插进去了,SELECT * FROM xxx也能查出来了,然后……就没有然后了。等真正接手业务需求,要统计销售额、要按地区拼接地址、要把时间字段做成报表维度时,突然发现SELECT和WHERE完全不够用。这时候你搜索出来的答案里,十个有八个都在用函数。MySQL 常用函数,就是把你从“能查数据”提升到“会处理数据”的那道分水岭。

这篇文章不聊安装、不聊配置,专门把 MySQL 里日常最高频的常用函数按场景拆开讲透,每个函数都配上可以直接抄的案例和实际踩坑记录。内容适合两类人:一类是刚入门、能写基础查询但遇到真实需求就卡壳的新手;另一类是写了很多年 SQL、但一直靠现搜现抄、没系统梳理过函数体系的开发者。把这些函数拿捏住,日常 80% 的数据处理需求都不在话下。

1. 为什么说常用函数是提升SQL效率的利器

1.1 函数的本质与SQL执行逻辑

函数本质上就是一个“黑匣子”:你给它一个或多个输入,它按固定规则处理完,给你一个确定的输出。SQL 里写函数,就是在数据流动的过程中加一层处理逻辑。听起来很简单,但很多人恰恰忽略了“数据流动”这四个字,导致对函数的使用时机和位置判断不准。

我见过不少零基础同学陷入一个误区:觉得只要能查出数据就行,函数这种东西等要用的时候再查。这个想法会让人长期停留在“复制粘贴”阶段。真实业务里几乎没有“直接查出来就能用”的数据,永远要清洗、要格式化、要计算、要汇总。把常用函数当成工具箱里的常备工具,而不是临时翻文档的应急品,写 SQL 的效率和心态会完全不同。

还需要理解函数出现的几个位置。在 MySQL 里,函数可以用在SELECT后面,影响结果展示;也可以用在WHERE后面,影响行过滤;还可以用在ORDER BY、GROUP BY、HAVING后面,影响排序和分组统计。不同位置处理的阶段不同,比如WHERE过滤发生在分组之前,HAVING过滤发生在分组之后,同一个函数放在不同位置,业务含义完全不一样。把这个先后顺序理清楚,函数才算真的用活了。

1.2 常用函数分类总览

MySQL 官方文档里的函数数量非常多,零基础没必要全背。我把日常实际使用频率最高的函数归成几个大类,每类记住几个核心函数,就能覆盖绝大多数业务场景。

函数类别核心函数典型用途
字符串函数CONCAT、SUBSTRING、REPLACE、TRIM、UPPER、LOWER、LEFT、RIGHT拼接、截取、清洗、格式化文本
数值函数ROUND、CEIL、FLOOR、ABS、MOD小数处理、取整、绝对值、取余
日期时间函数NOW、DATE_FORMAT、DATEDIFF、DATE_ADD、YEAR、MONTH获取当前时间、格式化、日期差值计算
条件逻辑函数IF、IFNULL、CASE WHEN按条件返回不同值、空值兜底
聚合函数COUNT、SUM、AVG、MAX、MIN分组汇总、统计全局数据

这个分类不是官方标准,而是我自己的使用经验总结。像类型转换函数CAST、CONVERT也很常用,但对零基础来说可以晚点再学,遇到具体报错再回来补,效果往往更好。后面的章节我会按这个顺序逐个展开,每个函数都会讲清楚“是什么、怎么用、坑在哪”。

2. 字符串函数:数据清洗与格式化的第一课

字符串函数是大部分人接触到的第一类函数,因为它最直观,而且几乎所有业务都离不开文本处理。用户姓名拼接、手机号打码、商品编码截取,全是字符串函数的活。

2.1 拼接与截取:CONCAT、SUBSTRING、LEFT、RIGHT

CONCAT是使用频率最高的字符串函数之一,作用是把两个或多个字符串拼成一个。实际业务里最常见的用法是把用户姓名和部门拼到一起展示,或者拼接完整地址。

SELECT CONCAT('张', '三', '同学') AS full_name; -- 输出:张三同学 SELECT CONCAT(emp_name, ' - ', dept_name) AS emp_info FROM employee;

这里有一个大坑必须提醒:CONCAT 遇到任何一个参数为 NULL,整个结果就是 NULL。我之前给业务方导用户地址,拼完发现大量行为 NULL,排查了很久才发现是部分用户的 district 字段为空。所以写拼接 SQL 之前,一定先确认源头字段是否有 NULL,有就用IFNULL或COALESCE兜底:

SELECT CONCAT(IFNULL(province, ''), IFNULL(city, '')) AS full_address FROM user_info;

提示:CONCAT 遇 NULL 即 NULL,这是新手最容易踩的坑之一。拼接前先用 IFNULL 或 COALESCE 做好兜底,别等到结果全是 NULL 才发现。

再来是截取函数。SUBSTRING(字符串, 起始位置, 长度)用得最多,经典场景是从身份证号里截取出生日期。注意 MySQL 的字符串位置从 1 开始,不是 0。第一次用的时候如果发现结果少了一位,十有八九是下标问题。

SELECT SUBSTRING(id_card, 7, 8) AS birth_date FROM user_info;

LEFT和RIGHT就简单得多,一个从左边截 N 个字符,一个从右边截 N 个字符。比如商品编码左边两位是分类编码,直接LEFT(code, 2)就能取出来;手机号后四位做安全展示,用RIGHT(phone, 4)。这类函数一眼就能看懂,但胜在写起来简洁,比SUBSTRING少写一个参数,我用得很频繁。

2.2 替换、去空格与大小写:REPLACE、TRIM、UPPER、LOWER

从外部导入的数据,最烦的就是字段里混着空格、换行,或者大小写不统一。REPLACE(列名, '要找的', '替换成')专门干这个活。比如手机号里有横杠,直接替换掉;分类名称里“旧版”要统一改成“经典版”,也可以用UPDATE配合REPLACE批量处理。

-- 去掉手机号里的横杠 SELECT REPLACE(phone, '-', '') FROM user_info; -- 把分类名称里的“旧版”统一改成“经典版” UPDATE product SET category = REPLACE(category, '旧版', '经典版');

TRIM是去两端空格函数,也能去掉指定字符。别小看它,从 Excel 导入的数据里经常带肉眼看不见的空格,直接导致WHERE name = '张三'匹配不到数据。遇到这种诡异问题,先对字段做一次TRIM再比较就好了。TRIM(BOTH '-' FROM '--abc--')还能把首尾的横杠去掉,处理用户输入的脏数据时很实用。

SELECT TRIM(name) FROM user_info; -- 去掉字符串首尾的指定符号 SELECT TRIM(BOTH '-' FROM '--abc--');

UPPER和LOWER是大小写转换。用户注册时可能输入大小写混合的邮箱,统计的时候统一LOWER一下再分组、去重,结果才会干净。不过需要提醒一句:如果在WHERE条件里对索引列使用函数,可能会导致索引失效。数据量小的时候没感觉,数据量大了查询会明显变慢。我的习惯是能不用函数就不在索引列上用函数,或者想办法在条件右侧用函数,减少对索引列的影响。

3. 数值处理与条件逻辑:计算和判断省心省力

字符串处理完了,接下来是数值和条件逻辑。这类函数看起来简单,但用好之后能让单个 SQL 的计算密度高很多,原本要在程序里写循环判断的逻辑,一条 SQL 就搞定了。

3.1 小数处理与符号运算:ROUND、CEIL、FLOOR、ABS、MOD

ROUND是四舍五入,CEIL是向上取整,FLOOR是向下取整,这三个的区别一定要分清。做价格展示的时候特别典型:商品价格 19.8 元,保留一位小数是 19.8,向上取整变 20,向下取整变 19。

SELECT price, ROUND(price, 1) AS round_1, CEIL(price) AS ceil_price, FLOOR(price) AS floor_price FROM product;

ROUND的第二个参数表示保留几位小数,可省略,默认保留 0 位。做金额计算时我建议明确写出来,因为ROUND(2.5)在 MySQL 里的结果是 3,有些数据库却返回 2,跨库迁移容易踩坑。ABS是绝对值,MOD是取余数。判断奇偶、分库分表取模、算两个账户金额差异的绝对值,都是这两个函数的经典场景。

-- 判断订单号奇偶,奇数走 A 通道处理 SELECT order_id, MOD(order_id, 2) AS channel FROM orders; -- 取两个账号金额差异的绝对值 SELECT ABS(acct1_balance - acct2_balance) AS diff FROM accounts;

数值函数看着简单,最容易出问题的是精度。货币金额我强烈建议用DECIMAL类型,计算完再ROUND;如果用FLOAT或DOUBLE算,很容易出现 0.30000000000000004 这种结果,对账场景下会出大事。这个坑我在刚做电商对账时踩过,深有体会。

3.2 条件判断函数:IF、IFNULL、CASE WHEN

IF(条件, 真值, 假值)是 SQL 里的三元表达式,结构简单但非常实用。比如判断订单金额是否超过 1000,直接打标签:

SELECT order_id, total_amount, IF(total_amount >= 1000, '大单', '普通单') AS order_type FROM orders;

IFNULL(expr, 默认值)专门处理 NULL:如果 expr 是 NULL,返回默认值,否则返回 expr 本身。很多初学者分不清IFNULL和IF的区别,其实一个是空值兜底,一个是条件判断,用途完全不同。还有个类似函数叫COALESCE,可以传多个参数,返回第一个非 NULL 的值,在多字段兜底时比IFNULL好用得多:

SELECT COALESCE(phone, mobile, '无联系方式') AS contact FROM user_info;

CASE WHEN是 SQL 里最强大的条件表达式,没有之一。IF只能处理一个条件,CASE WHEN可以写多个分支,逻辑清晰、易维护。面试和实际业务里都爱考。比如把订单按金额分成多个等级:

SELECT order_id, total_amount, CASE WHEN total_amount < 100 THEN '小额订单' WHEN total_amount < 1000 THEN '中额订单' WHEN total_amount < 5000 THEN '大额订单' ELSE '超大额订单' END AS order_level FROM orders;

CASE WHEN的判定顺序是从上往下,一旦命中就会跳出,不再往下匹配。所以写条件时要从范围小的往范围大的写,或者确保条件互斥。我有一次把total_amount >= 1000写在前面,结果所有超千订单全被算进了“大额”,后面的分支根本走不到,最后只能重跑任务。还有一个细节:END后面一定记得写列别名,不然查询结果里那列表头会是一长串表达式,程序里引用时非常难受。

4. 日期时间函数:业务统计里永远绕不开的坑

日期时间是 SQL 里最容易出错、也最有价值的一类函数。业务报表按天、按月、按年统计,全靠日期函数转换和分组。零基础同学通常觉得日期函数数量太多、记不住。我建议先记最常用的几个,用多了自然就熟练了。

4.1 获取当前时间与时间戳转换

获取当前时间有三个高频函数:NOW()返回完整日期时间,CURDATE()只返回日期部分,CURTIME()只返回时间部分。还有一个细节:NOW()在同一个查询里多次调用,返回的是同一个时间点;SYSDATE()则不同,每次调用都可能变化。这个差异很小,但某些对时间一致性要求高的场景会踩到。

SELECT NOW() AS current_datetime, CURDATE() AS current_date, CURTIME() AS current_time;

时间戳转换在对接第三方数据时非常常见。UNIX_TIMESTAMP把日期时间转成秒级时间戳,FROM_UNIXTIME把秒级时间戳转回可读日期。很多接口返回的就是时间戳,而数据库里存的是日期时间,这两个函数就是桥。

SELECT UNIX_TIMESTAMP('2024-06-01 12:00:00'); -- 输出秒级时间戳 SELECT FROM_UNIXTIME(1717214400); -- 输出可读日期时间

4.2 格式化、差值计算与日期偏移

DATE_FORMAT是日期函数里的“门面”,把日期时间按指定格式重新包装。业务报表用得最多的是按月分组统计:

SELECT DATE_FORMAT(create_time, '%Y-%m') AS month, COUNT(*) AS order_cnt FROM orders GROUP BY DATE_FORMAT(create_time, '%Y-%m') ORDER BY month;

格式化符号一定要记牢:%Y是四位年,%y是两位年,%m是两位月,%d是两位日,%H是 24 小时制小时,%i是分钟,%s是秒。容易混淆的是%Y和%y、%H和%h(后者是 12 小时制)。如果格式化结果不对,先检查符号是否写对,我经常看到有人把%m当成分钟用,查半天才发现是符号理解错了。

DATEDIFF(结束日期, 开始日期)计算两个日期相差的天数,在会员有效期、订单超时判断里很常用。DATE_ADD和DATE_SUB是日期偏移函数,第二个参数用INTERVAL加上时间量。计算最近 30 天的数据范围是最典型场景:

SELECT DATE_SUB(CURDATE(), INTERVAL 30 DAY) AS start_date, CURDATE() AS end_date;

INTERVAL支持的单位很丰富,DAY、MONTH、YEAR、HOUR、MINUTE、SECOND都行。做定时任务或滑动窗口统计时,这组函数非常好使。这里还有一个使用心得:日期函数几乎都会受时区影响,线上数据库时区和服务器时区不一致,拿到“当前时间”可能和业务方实际时间差好几个小时。如果业务对时间敏感,建议统一用数据库所在时区,或者干脆存 UTC 时间,展示层再转换,这样最稳。

5. 聚合函数与分组统计:从“看单条”到“看全局”

前面讲的函数都是对单行数据做处理,聚合函数则完全不同:它把多行数据合并成一个结果。这种“从单条到全局”的思路转变,是 SQL 进阶的重要一步,也是报表统计的基础。

5.1 COUNT、SUM、AVG、MAX、MIN的适用场景

五个核心聚合函数:COUNT统计行数,SUM求和,AVG求平均,MAX取最大,MIN取最小。别看名字简单,细节非常多,面试里最爱问的就是这些“听起来简单”的函数。

SELECT COUNT(*) AS total_cnt, COUNT(user_id) AS valid_user_cnt, SUM(amount) AS total_amount, AVG(amount) AS avg_amount, MAX(amount) AS max_amount, MIN(amount) AS min_amount FROM orders;

COUNT(*)和COUNT(列名)的区别是最大的坑。COUNT(*)统计所有行,包括 NULL 所在行;COUNT(列名)只统计该列非 NULL 的值的数量。比如统计用户表里的手机号数量,如果直接用COUNT(phone),而手机号存在缺失,结果比COUNT(*)少。你以为自己在统计用户总数,实际上统计的是“有手机号的用户数”,这种 bug 很隐蔽。

SUM和AVG会自动忽略 NULL 值,但整列全是 NULL 时SUM返回 NULL 而不是 0。AVG也一样,不想看到 NULL 就用IFNULL兜底。MAX和MIN对字符串和数字都能比较,但要注意类型一致性,字符串比较按字典序来。还有一个高频操作是去重统计,用COUNT(DISTINCT 列名)统计去重后的数量,比如统计去重用户数,这种“先想去重,再想统计”的思路很实用。

5.2 GROUP BY + HAVING 的正确使用姿势

GROUP BY是聚合函数的最佳搭档。先分组、再聚合,是报表的基本逻辑。按部门统计人数是最经典的例子:

SELECT dept_id, COUNT(*) AS emp_cnt, AVG(salary) AS avg_salary FROM employee GROUP BY dept_id;

GROUP BY有两个容易踩的坑。第一,SELECT后面的非聚合列必须出现在GROUP BY里。MySQL 老版本对这个问题管得不严,可能返回随机行的值,但同样一条 SQL 放到 MySQL 8.0 或 PostgreSQL 里就直接报错。我见过不少同学靠着老版本 MySQL 的“宽容”写出不规范 SQL,一旦迁移就崩,所以从一开始就养成规范习惯很重要。

第二,WHERE和HAVING的区别。WHERE在分组之前过滤行,HAVING在分组之后过滤组。看这个例子:

-- 筛选金额大于100的订单,按用户汇总,最后只看汇总金额超过500的用户 SELECT user_id, SUM(amount) AS total_amount FROM orders WHERE amount > 100 GROUP BY user_id HAVING SUM(amount) > 500;

把HAVING里的条件误写到WHERE里,比如WHERE SUM(amount) > 500,会直接报错,因为执行WHERE时聚合结果还没出来。反过来,把单行条件amount > 100写在HAVING里虽然不报错,但性能会差很多,因为所有行都参与了分组聚合才被过滤。我优化过很多慢查询,不少就是HAVING里塞了单字段条件,移到WHERE后速度立马上来。

6. 进阶组合技巧与常见问题排查

基础函数学完之后,还要学会组合使用。SQL 函数最大的价值恰恰在组合,单个函数效果有限,组合起来才能处理复杂业务需求。这一节分享几个我实际工作中高频使用的组合技巧,顺手把新手最爱踩的坑整理成速查表。

6.1 函数嵌套与GROUP_CONCAT的妙用

函数嵌套就是把一个函数的结果作为另一个函数的输入。比如DATE_FORMAT和SUM配合可以按月汇总,IF和COUNT配合可以做条件计数:

-- 统计每个品类下大额订单占比 SELECT category_id, COUNT(IF(total_amount >= 1000, 1, NULL)) AS big_order_cnt, COUNT(*) AS total_cnt, ROUND(COUNT(IF(total_amount >= 1000, 1, NULL)) / COUNT(*), 4) AS big_order_ratio FROM orders GROUP BY category_id;

这个写法很常用。COUNT(IF(条件, 1, NULL))的意思是:满足条件的行计为 1,不满足置为 NULL,而COUNT忽略 NULL,所以统计结果就是满足条件的行数。注意这里不能写成COUNT(IF(条件, 1, 0)),否则COUNT会把 0 一起数进去,结果和COUNT(*)一样。

GROUP_CONCAT是一个非常有用的函数,能把分组内的多行数据拼成一个字符串。比如把每种技能对应的用户名列出来:

SELECT skill, GROUP_CONCAT(user_name ORDER BY user_id SEPARATOR '、') AS users FROM user_skill GROUP BY skill;

GROUP_CONCAT有几个细节要记住:SEPARATOR指定分隔符,默认是逗号;ORDER BY可以控制拼接顺序;最坑的是默认最大长度只有 1024 字节,超过会被截断。如果拼接结果不全,先检查是不是长度超了,可以用SET SESSION group_concat_max_len = 102400调整。另外,拼接时可以用DISTINCT去重,比如GROUP_CONCAT(DISTINCT dept_name),避免重复项出现在结果里。

6.2 常见问题速查表与避坑经验

下面把这些阶段最常见的坑整理成速查表,都是实际跑 SQL 时容易遇到的问题:

现象根本原因解决办法
CONCAT 拼接结果出现 NULL某个参数字段为 NULL用 IFNULL 或 COALESCE 兜底
COUNT 结果比预期少使用了 COUNT(列名) 而该列有 NULL明确统计意图,必要时用 COUNT(*)
GROUP BY 后选了不在分组里的列非聚合列未包含在 GROUP BY 中把该列加入 GROUP BY,或改成聚合值
单行条件写在 HAVING 里导致慢查询混淆了分组前后过滤单行条件移到 WHERE
日期格式化结果不对%y 和 %Y、%h 和 %H 混淆检查格式化符号写法
GROUP_CONCAT 结果被截断超过 1024 字节限制调整 group_concat_max_len
CASE WHEN 分支结果异常条件顺序写错,先命中了小范围分支从小到大书写或保证条件互斥
金额计算出现小数误差使用 FLOAT/DOUBLE 存储金额金额字段改 DECIMAL 类型
查询结果出现奇怪数字隐式类型转换导致明确用 CAST 指定类型

最后给一条贯穿所有函数的使用心法:先看数据,再写函数。写 SQL 之前,先把字段的数据类型、有没有 NULL、数据格式是否统一摸清楚。不管函数多强大,源头数据是乱的,后面清洗起来都费劲。有个小伙伴写了个很长的 SQL,用了六七个函数,跑出来结果还是不对。我让他先把原始数据SELECT出来看一眼,立刻就发现日期字段里混着0000-00-00和正常日期,所有涉及日期计算的函数结果全是 NULL。数据源不干净,函数写得再认真也是白搭。

我个人带过不少零基础的同学,最深的体会是:MySQL 常用函数不是背出来的,是一条查询一条查询“磨”出来的。别指望看完这篇文章就把所有函数记住,这不现实。更靠谱的做法是,今天先挑四个最常用的——CONCAT、ROUND、DATE_FORMAT、COUNT,在自己业务数据上跑一遍,跑通了再往下加。踩过的坑,都会变成你的经验。

等这些基础函数用顺手了,再去碰窗口函数、存储过程这些高级特性,会轻松很多。函数就是 SQL 的地基,地基牢了,后面学什么都踏实。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询