ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MySQL常用函数详解:字符串、日期、聚合一次搞定

MySQL常用函数详解:字符串、日期、聚合一次搞定 1. 为什么说常用函数是 MySQL 提效的第一站刚接触 MySQL 的朋友最容易掉进一个误区认为 SQL 就是查数据、改数据只要会SELECT、INSERT、UPDATE、DELETE就够了。等你真正处理过几千行甚至几十万行数据你就会发现不会用函数的人写出的 SQL 又臭又长甚至要先把数据拉回程序里用 Python、Java 做一轮循环处理效率低不说还容易出错。我经常跟学员举一个例子一张订单表里存了用户手机号格式是138-1234-5678老板要求按手机号去重统计用户数。不会函数的人怎么办先SELECT出来到 Python 里面.replace(-, )再塞回 SQL 做COUNT(DISTINCT ...)。会函数的人一条 SQL 就解决了COUNT(DISTINCT REPLACE(phone, -, ))。差距就是这么明显。这篇文章我按零基础的学习路径把 MySQL 里最常用的几类函数讲透。每一类函数我都给你讲清楚它是干什么的、怎么用、实际业务里什么时候用、有没有坑。你在 Navicat 或命令行里跟着敲一遍会比看十遍理论都管用。先说一个总的原则MySQL 函数分为单行函数和聚合函数两大类。单行函数就是“一行数据进去一个结果出来”比如把字符串转大写、把日期格式化聚合函数是“多行数据进去一个结果出来”比如求和、求平均值、统计行数。这两类函数的思维方式完全不同前者是逐行变换后者是分组压缩先把这个概念刻在脑子里后面学起来就顺了。另外说句掏心窝的话函数这东西不需要背。你需要的是“知道它存在”用的时候回来查用几次就记住了。但有几类高频函数我建议你当成“肌肉记忆”一样熟练掌握——字符串处理、日期运算、条件判断、聚合统计这四块是日常写 SQL 的绝对主力。2. 字符串函数拼接、截取、替换一网打尽字符串函数是 MySQL 里使用频率最高的一类因为它直接处理业务中的脏数据、不标准格式、文本拼接等需求。零基础入门先掌握下面这 12 个就够用了。2.1 字符串拼接CONCAT 和 CONCAT_WSCONCAT(str1, str2, ...)是最基础的拼接函数。比如说用户表里分别存了first_name和last_name想查全名就可以这样写SELECT CONCAT(first_name, last_name) AS full_name FROM users;这里有一个零基础最容易踩的坑如果拼接的字段里有 NULLCONCAT 的结果会变成 NULL。比如用户没有填姓氏last_name是 NULL那么整条拼接结果都是 NULL而不是只剩名字。我见过不止一个同事因为这个在报表里查出一堆空数据。解决办法有两个。一个是先判断再拼接SELECT CONCAT(IFNULL(first_name, ), IFNULL(last_name, )) FROM users;另一个是用CONCAT_WS它的全称是 Concatenate With Separator意思是带分隔符的拼接。它有个很好的特性会自动跳过 NULL不会因为某个字段是 NULL 就把整条结果变成 NULL。SELECT CONCAT_WS( , first_name, last_name) FROM users;这个函数在生成地址、拼接姓名、拼接导出文件名等场景下特别实用。2.2 截取函数LEFT、RIGHT 和 SUBSTRING截取字符串是数据处理里躲不开的操作。比如身份证号里要取出生年月日或者手机号要脱敏显示前三位后四位。LEFT(str, n)从左边开始取 n 个字符。RIGHT(str, n)从右边开始取 n 个字符。SUBSTRING(str, pos, len)从第 pos 个位置开始取 len 个字符位置从 1 开始数。举个脱敏的例子SELECT CONCAT(LEFT(phone, 3), ****, RIGHT(phone, 4)) AS masked_phone FROM users;比如原手机号13812345678结果就是138****5678。这里要注意MySQL 的SUBSTRING位置从 1 开始不是从 0 开始和 Python、JavaScript 的习惯不一样写代码的时候容易犯迷糊。如果要从中间某个位置截到末尾可以省略第三个参数SUBSTRING(str, pos)。比如字符串abcdefSUBSTRING(abcdef, 3)的结果是cdef。2.3 替换函数REPLACE 的妙用REPLACE(str, from_str, to_str)是把字符串里指定的子串替换成新内容。文章开头那个手机号去重案例用的就是它SELECT COUNT(DISTINCT REPLACE(phone, -, )) AS user_cnt FROM orders;REPLACE 是全局替换也就是说字符串里出现了多少次目标子串它就替换多少次。比如把文案里所有的“你”替换成“您”一条 SQL 就能批量处理。还有几个高频字符串函数我直接列成一个速查表函数作用示例结果UPPER(str)转大写UPPER(mysql)MYSQLLOWER(str)转小写LOWER(MYSQL)mysqlLENGTH(str)返回字节数LENGTH(abc)3CHAR_LENGTH(str)返回字符数CHAR_LENGTH(你好)2TRIM(str)去掉首尾空格TRIM( mysql )mysqlLPAD(str, n, pad)左侧填充到指定长度LPAD(7, 3, 0)007LENGTH 和 CHAR_LENGTH 这个区别值得多说一句。LENGTH 是字节长度一个中文汉字在 utf8mb4 编码下占 3 个字节所以LENGTH(你好)返回 6而CHAR_LENGTH(你好)返回 2。涉及中文数据处理校验长度时优先用 CHAR_LENGTH否则你按字节截断容易把汉字截成乱码。2.4 实际业务里字符串函数怎么组合用字符串函数单独用没感觉组合起来才见功力。比如用户表里有一个address字段存的是省市区拼接的完整地址现在要单独统计省份分布SELECT SUBSTRING_INDEX(address, 省, 1) AS province, COUNT(*) AS cnt FROM users GROUP BY SUBSTRING_INDEX(address, 省, 1);SUBSTRING_INDEX(str, delim, count)是按分隔符截取count 为正数表示从左数第几个分隔符之前的内容负数表示从右数。这是处理层级类文本的利器。上面这种写法虽然要求地址里必须有“省”字才严谨但思路很典型先用函数把原始数据里的特征提取出来再配合分组统计进行分析。再举一个实际排查数据的场景。运营同学反馈订单备注里有人填了联系电话格式五花八门有的是手机:138...有的是联系 138...。想统一提取手机号正则表达式最专业但零基础先用基础函数也能做个七八分先定位1的位置再截取 11 位。这里先不展开正则表达式后面有专文介绍。3. 数值函数与日期函数别再把格式化算好的活揽到代码里很多从 Java、Python 转过来写 SQL 的朋友有个共同毛病日期格式化、数值取整这类逻辑习惯性地放到程序里做觉得数据库只负责存取数据。实际上 MySQL 内置的数值函数和日期函数已经非常强大在 SQL 里直接算既能减少网络传输量又能让统计逻辑集中在数据库层排查问题时也更方便。3.1 数值函数ROUND、CEIL、FLOOR 与 MOD数值函数里日常用到最多的就几个ROUND(x, d)四舍五入d 是保留的小数位数。ROUND(3.14159, 2)结果是 3.14。CEIL(x)/CEILING(x)向上取整哪怕小数部分是 0.01 也进位。CEIL(3.01)结果是 4。FLOOR(x)向下取整FLOOR(3.99)结果是 3。MOD(a, b)取余数MOD(10, 3)结果是 1。这几个函数单价看着简单实际业务里很容易用错场景。比如设置分页的时候不用 MySQL 自带的LIMIT offset, rows而是自己算起始位置有人就会搞混向上取整和向下取整的适用场景。我踩过的一个真实的坑是 ROUND 在银行金额计算里的精度问题。MySQL 的ROUND在遇到.5这种边界情况时行为可能和你的直觉不一样。比如ROUND(2.5)和ROUND(3.5)在 MySQL 中前者结果是 3后者结果是 4看起来正常但在某些浮点运算场景下ROUND(2.55, 1)可能得到 2.5 而不是 2.6因为浮点数的二进制表示不是精确的。涉及金额计算我的建议是整数用分存储避免直接对浮点数做四舍五入实在要用小数先CAST成DECIMAL类型再算。这是我在金融项目里用真金白银换来的经验。3.2 日期函数NOW、DATE_FORMAT、DATEDIFF 与 DATE_ADD日期函数的重要性不需要多讲——几乎任何业务表里都有created_at这类时间字段而报表分析最少不了的就是“昨天”“本周”“上个月”这类时间范围。先记住几个最常用的-- 当前时间 SELECT NOW(); -- 2025-01-15 10:30:00 SELECT CURDATE(); -- 2025-01-15 SELECT CURTIME(); -- 10:30:00 -- 格式化日期 SELECT DATE_FORMAT(2025-01-15 10:30:00, %Y-%m-%d %H:%i:%s); -- 2025-01-15 10:30:00 SELECT DATE_FORMAT(NOW(), %Y-%m-%d); -- 2025-01-15DATE_FORMAT的格式符是新手最懵的地方。我这里整理了一张高频格式符对照表你保存好格式符含义示例%Y四位年份2025%y两位年份25%m月份不足两位补零01%c月份不补零1%d日不足两位补零05%H24 小时制小时13%h12 小时制小时01%i分钟30%s秒00%W星期几的英文名Wednesday日期计算函数是另一个重头。面试里经常考业务里也常用-- 加减天数 SELECT DATE_ADD(2025-01-15, INTERVAL 7 DAY); -- 2025-01-22 SELECT DATE_SUB(2025-01-15, INTERVAL 1 MONTH); -- 2024-12-15 -- 两个日期相差天数 SELECT DATEDIFF(2025-01-20, 2025-01-15); -- 5注意DATEDIFF是前一个日期减后一个日期所以DATEDIFF(2025-01-15, 2025-01-20)结果是 -5。网上很多人搜“mysql将字符串转为日期”其实就是STR_TO_DATE函数它和DATE_FORMAT是反操作SELECT STR_TO_DATE(2025-01-15 10:30:00, %Y-%m-%d %H:%i:%s);这个函数在处理外部导入的文本型日期时特别有用。比如 Excel 导出的 CSV 里日期常常是2025/1/15这种格式你就可以SELECT STR_TO_DATE(2025/1/15, %Y/%m/%d);3.3 日期函数实战按天统计订单的三种写法统计每天的订单量是面试和实际工作里的基础题目。有三条 SQL 都能实现但各有利弊写法一用 DATE_FORMATSELECT DATE_FORMAT(created_at, %Y-%m-%d) AS day, COUNT(*) FROM orders WHERE created_at 2025-01-01 AND created_at 2025-02-01 GROUP BY DATE_FORMAT(created_at, %Y-%m-%d);写法二用 DATE 函数直接截断SELECT DATE(created_at) AS day, COUNT(*) FROM orders WHERE created_at 2025-01-01 AND created_at 2025-02-01 GROUP BY DATE(created_at);两种写法结果相同但性能上有微妙差别。如果created_at上有索引写法二更容易走到索引范围扫描写法一相当于对每一行先做格式化再分组索引往往失效。数据量小无所谓到了百万级这个差异能让你查询从 0.5 秒变成 5 秒。我建议你在实际写统计 SQL 时尽量用DATE(created_at)或直接对时间字段做范围过滤而不是对字段套一层函数。另外支付宝、微信支付的对账文件里经常用%Y%m%d这种紧凑格式比如20250115处理方法是SELECT DATE_FORMAT(2025-01-15 10:30:00, %Y%m%d); -- 20250115 SELECT STR_TO_DATE(20250115, %Y%m%d); -- 2025-01-154. 条件判断函数IF 与 CASE WHEN 的实战套路条件判断函数是写业务 SQL 绕不开的核心。它解决的问题是同一张表里的数据要根据不同条件展示成不同结果。比如用户表里有个vip_level字段0 是普通用户1 是会员2 是超级会员你不能直接把数字扔给前端得在 SQL 里先把数字翻译成文字。4.1 IF 函数最简条件判断IF(expr, val_if_true, val_if_false)是最简单的条件分支只有两个结果。SELECT name, IF(vip_level 0, 会员, 普通用户) AS user_type FROM users;这个函数适合二选一场景。比如查询用户是否成年SELECT name, IF(age 18, 成人, 未成年) AS age_group FROM users;IF 也经常和聚合函数配合实现有条件的统计。比如统计会员和非会员的订单量SELECT IF(vip_level 0, 会员, 非会员) AS user_type, COUNT(*) AS order_cnt FROM orders GROUP BY IF(vip_level 0, 会员, 非会员);4.2 CASE WHEN多分支判断的正确打开方式当判断条件超过两个分支IF 的嵌套写法会变得非常难看比如IF(a, x, IF(b, y, IF(c, z, w)))读起来像绕口令。这种情况就该用CASE WHEN。CASE WHEN 有两种写法。第一种是表达式模式适合等值判断SELECT name, CASE vip_level WHEN 0 THEN 普通用户 WHEN 1 THEN 会员 WHEN 2 THEN 超级会员 ELSE 未知 END AS user_type FROM users;第二种是条件模式适合范围判断SELECT name, CASE WHEN age 18 THEN 少年 WHEN age 18 AND age 30 THEN 青年 WHEN age 30 AND age 60 THEN 中年 ELSE 老年 END AS age_group FROM users;第二种更灵活条件之间可以写任意表达式。注意一点CASE WHEN 是自顶向下匹配的匹配到第一个为真的条件就停止不再往下判断。所以写条件的时候顺序很重要要把最精确的条件放前面。4.3 条件函数在报表统计中的妙用条件判断函数真正体现功力的时候是在报表统计里做“横向转置”。比如你要统计不同支付方式的订单数和金额占比原始方式是按支付方式分组显示成多行如果要把每种支付方式显示成一列就需要和聚合函数配合SELECT DATE_FORMAT(created_at, %Y-%m-%d) AS day, SUM(CASE WHEN pay_type alipay THEN amount ELSE 0 END) AS alipay_amount, SUM(CASE WHEN pay_type wechat THEN amount ELSE 0 END) AS wechat_amount, SUM(CASE WHEN pay_type card THEN amount ELSE 0 END) AS card_amount, SUM(amount) AS total_amount FROM orders GROUP BY DATE_FORMAT(created_at, %Y-%m-%d);这样每天一行各种支付金额在同一行展示Excel 透视表都不用开了直接导出就是日报。这个写法在零售、电商、财务对账场景里非常常见属于“必会套路”。还有一类场景判断空值。IFNULL(expr1, expr2)是判断第一个参数是否为 NULL是则返回第二个值。比如用户表里nickname可能没填展示的时候要用默认值SELECT id, IFNULL(nickname, 匿名用户) AS display_name FROM users;另外一个容易被忽略但很实用的是NULLIF(expr1, expr2)它和 IFNULL 相反是当两个参数相等时返回 NULL。比如除数为 0 的保护SELECT amount / NULLIF(quantity, 0) AS unit_price FROM orders;quantity 为 0 时NULLIF返回 NULL整个除法结果变成 NULL不会报“除以零”的错。5. 聚合函数与分组统计报表查询的核心引擎进入聚合函数意味着你已经从“取数据”迈向“算数据”了。聚合函数的特点是把多行记录压缩成一个结果。这类函数在报表、统计、分析场景里是绝对主力也是面试中 GROUP BY 相关题目必考的底层能力。5.1 五大聚合函数与空值的处理规则MySQL 基本的聚合函数就五个函数作用注意事项COUNT(*)统计行数包括 NULL 行速度通常最快COUNT(col)统计指定列的非 NULL 值个数自动忽略 NULLSUM(col)求和自动忽略 NULL全为 NULL 返回 NULLAVG(col)求平均值自动忽略 NULL不是除以总行数MAX(col)/MIN(col)最大值 / 最小值自动忽略 NULL这里面有个特别容易翻车的点AVG 会自动忽略 NULL而不是把 NULL 当 0 处理。举个例子5 个学生的成绩分别是 90、80、70、NULL、NULLAVG(score)的结果是(908070)/3 80而不是除以 5 得到 56。如果你希望 NULL 按 0 参加平均必须显式转换SELECT AVG(IFNULL(score, 0)) FROM students;这个细节在算员工绩效、设备在线率、考试平均分等场景中非常关键。我见过有报表把在线率算错最后排查半天根因就是 AVG 忽略 NULL 的行为。5.2 GROUP BY 和聚合函数的配合逻辑GROUP BY 加上聚合函数是统计查询的核心语法。它的执行逻辑初学者容易理解错我拆开讲一下先按 GROUP BY 后面的字段把相同值的行分到一组。每组分别执行 SELECT 里的聚合函数。每组只输出一行结果。比如统计每个城市的用户数SELECT city, COUNT(*) AS user_cnt FROM users GROUP BY city;执行逻辑就是“先按城市分组再数每组有多少行”。注意SELECT 中出现的非聚合列必须出现在 GROUP BY 中。MySQL 有个特殊例外默认允许 SELECT 一个不在 GROUP BY 里的列结果是从该组中随机取一行非常容易造成数据混乱。我强烈建议零基础学 SQL 时坚持遵守“非聚合字段必须出现在 GROUP BY 中”这条 SQL 标准规则不要依赖 MySQL 的宽松行为。5.3 HAVING 与 WHERE筛选时机完全不同WHERE 和 HAVING 都能做筛选但执行时机完全不同。WHERE 在分组之前过滤行HAVING 在分组之后过滤组。这意味着WHERE 里不能写聚合函数HAVING 里可以。查订单数超过 10 个的用户SELECT user_id, COUNT(*) AS order_cnt FROM orders GROUP BY user_id HAVING COUNT(*) 10;这里不能把COUNT(*) 10写在 WHERE 里因为分组还没发生聚合值还计算不出来。再举一个 WHERE 和 HAVING 配合使用的完整例子。查出 2025 年 1 月订单数超过 5 个的用户SELECT user_id, COUNT(*) AS order_cnt FROM orders WHERE created_at 2025-01-01 AND created_at 2025-02-01 GROUP BY user_id HAVING COUNT(*) 5;WHERE 先过滤出 1 月的数据GROUP BY 分组HAVING 再过滤掉订单数不满 5 个的组。整个过程清晰明了。还有一个性能相关的点GROUP BY 或聚合计算之前先用 WHERE 把不需要的行过滤掉会大大减少分组时的数据量。所以写 SQL 的一个习惯是能写在 WHERE 里的条件就不要留到 HAVING 里。5.4 聚合函数配合 DISTINCT 的去重统计统计去重后的数量是报表里非常高频的操作。比如统计有订单的用户总数SELECT COUNT(DISTINCT user_id) FROM orders;如果不加 DISTINCTCOUNT(user_id)统计的是订单行数不是用户数。写之前要先想清楚“我要数的到底是行还是人”。DISTINCT 也可以和 GROUP BY 配合比如统计每天下单的用户数SELECT DATE(created_at) AS day, COUNT(DISTINCT user_id) FROM orders GROUP BY DATE(created_at);这里背后隐藏的一句解释是COUNT(DISTINCT user_id)是“每组内先去重 user_id再计数”。6. 综合案例一张订单表把函数串起来前面讲了六大类函数单独看都明白但真正会用还得靠综合案例把它们串起来。我模拟一个电商订单管理场景用一张orders表把今天学的函数全部融进一个查询里。先建表并插入测试数据CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT, product_name VARCHAR(50), category VARCHAR(20), amount DECIMAL(10,2), pay_type VARCHAR(10), created_at DATETIME ); INSERT INTO orders (user_id, product_name, category, amount, pay_type, created_at) VALUES (1, 机械键盘, 数码, 399.00, alipay, 2025-01-05 10:15:00), (1, 显示器支架, 配件, 89.90, wechat, 2025-01-12 14:30:00), (2, 无线鼠标, 数码, 129.00, alipay, 2025-01-12 16:20:00), (2, 手机壳, 配件, 39.90, card, 2025-02-03 09:45:00), (3, 机械键盘, 数码, 399.00, wechat, 2025-02-08 21:10:00), (3, USB扩展坞, 配件, 159.00, alipay, 2025-02-15 11:30:00);场景一统计每个用户的订单总额、订单数、首单日期并给用户打标签。下单次数超过 2 次的标记为“高频用户”否则是“普通用户”SELECT user_id, COUNT(*) AS order_cnt, SUM(amount) AS total_amount, MIN(created_at) AS first_order_time, CASE WHEN COUNT(*) 2 THEN 高频用户 ELSE 普通用户 END AS user_level FROM orders GROUP BY user_id;这个查询里同时用了 COUNT、SUM、MIN 三个聚合函数而且CASE WHEN里嵌套了聚合函数COUNT(*)这在逻辑上是允许的先分组聚合再对聚合结果做条件判断。SQL 的执行顺序是分组在前判断在后配合得刚刚好。场景二按支付方式汇总同时展示每种支付方式的订单数、总额、最大单笔金额、占到全局总额的百分比SELECT IFNULL(pay_type, 未知) AS pay_type, COUNT(*) AS order_cnt, SUM(amount) AS total_amount, MAX(amount) AS max_amount, CONCAT(ROUND(SUM(amount) / (SELECT SUM(amount) FROM orders) * 100, 1), %) AS amount_pct FROM orders GROUP BY pay_type ORDER BY total_amount DESC;这里用到了 IFNULL 处理空值、COUNT、SUM、MAX 聚合、ROUND 保留一位小数、CONCAT 拼上百分号还嵌套了一个子查询算全局总额。一条 SQL 把报表统计中 80% 的需求都覆盖了。场景三找出 2025 年 1 月的订单按商品分类统计销量并且只保留累计金额大于 100 元的分类最后按金额降序排SELECT category, COUNT(*) AS sale_cnt, SUM(amount) AS sale_amount FROM orders WHERE created_at 2025-01-01 AND created_at 2025-02-01 GROUP BY category HAVING SUM(amount) 100 ORDER BY sale_amount DESC;这个查询完整走了一遍 WHERE 过滤 → GROUP BY 分组 → 聚合计算 → HAVING 过滤 → ORDER BY 排序的全流程。你把它在 Navicat 里跑一遍再对照结果逆向思考每一步的执行逻辑对理解 SQL 的帮助比看十遍理论都大。场景四订单日期加上一个“发货时限”的需求下单后 3 天内发货给每笔订单算出最迟发货日期SELECT id, product_name, created_at, DATE_ADD(created_at, INTERVAL 3 DAY) AS deadline FROM orders;再用 DATEDIFF 算到现在还有几天SELECT id, product_name, created_at, DATEDIFF(DATE_ADD(created_at, INTERVAL 3 DAY), NOW()) AS days_left FROM orders;这就是日期函数和业务规则结合的实际用法。你可以在命令行里把这些案例敲一遍感受一下“一个需求对应脑子里冒出的哪个函数”这个过程。7. 零基础最容易踩的坑与调试建议函数学了一堆真正写的时候还是会翻车。我把自己带新人过程中反复遇到的坑集中梳理一遍每个都附上对应的排查建议。7.1 NULL 传染问题NULL 参与几乎所有运算结果都会变成 NULL。字符串拼接遇到 NULL 返回 NULL加减乘除遇到 NULL 返回 NULLIF(expr, a, b)里 expr 为 NULL 时会被当成假值处理但NULLIF(expr1, expr2)的返回又会让你防不胜防。处理建议涉及可能为空的字段先想清楚“如果这里是 NULL结果应该是什么”然后主动用 IFNULL、COALESCE 或 CONCAT_WS 兜底。COALESCE 函数值得认识一下它可以依次返回第一个非 NULL 值COALESCE(NULL, NULL, abc, def)结果是abc比嵌套多个 IFNULL 清爽得多。7.2 分组字段和查询字段不一致MySQL 默认允许 SELECT 中出现不在 GROUP BY 里的普通列结果是从该分组中随机取一条而且不同版本行为还不一样这简直是隐形的数据陷阱。比如SELECT user_id, product_name, COUNT(*) FROM orders GROUP BY user_id;如果同一个用户买过多个商品product_name显示哪个完全随机。这在 5.7 和 8.0 中的行为也有差异。建议你从第一天写 SQL 就养成习惯SELECT 里出现的非聚合列全部放到 GROUP BY 里。如果只想随机取一个商品名那就用MAX(product_name)或SUBSTRING_INDEX(GROUP_CONCAT(product_name), ,, 1)这类显式方式控制行为。7.3 时间范围查询的边界问题统计某天数据时很多人习惯写WHERE created_at 2025-01-15如果created_at是 DATETIME 类型这一行数据的时间戳是2025-01-15 08:30:00直接等于2025-01-15匹配不到因为字符串会被转成2025-01-15 00:00:00两者不相等。正确做法是范围查询WHERE created_at 2025-01-15 AND created_at 2025-01-16这样能包含当天所有时刻的数据而且能利用索引。另一个原因是DATE(created_at) 2025-01-15虽然结果对但函数套在字段上会破坏索引使用千万级数据量时性能差异明显。7.4 忘记对查询结果做类型转换DATE_FORMAT 返回的是字符串SUM 返回的是 DECIMAL理解这一点在做联表查询或结果比对时很重要。比如MAX(amount)的类型是字段原类型如果想精确到两位小数展示就要包一层SELECT CAST(MAX(amount) AS DECIMAL(10,2)) FROM orders;CAST 和 CONVERT 函数在类型转换时很常用SELECT CAST(123 AS UNSIGNED); -- 123 SELECT CONVERT(2025-01-15, DATE); -- 2025-01-157.5 调试 SQL 的实用习惯SQL 报错时错误信息往往很模糊。我分享一下自己排查问题的顺序第一先单独跑函数部分。不确定DATE_FORMAT的格式符对不对就把函数单独 SELECT 出来看结果不要嵌在复杂的查询里调。比如在 Navicat 里单独执行SELECT DATE_FORMAT(NOW(), %Y-%m);秒级确认。第二把 GROUP BY 的查询拆成两步。先不加 GROUP BY只 SELECT 原始字段看看每组的数据长什么样再决定用哪个聚合函数。这能避免很多“结果和预期不符”的困惑。第三用EXPLAIN看执行计划。在 SELECT 前面加上EXPLAIN关键字MySQL 会告诉你查询走没走索引、扫描了多少行、有没有临时表。这是性能优化最基础的手段哪怕是零基础也应该养成看执行计划的习惯。第四对函数参数做测试时多用常量而不是字段。比如在不确定SUBSTRING_INDEX的负数参数行为时直接SELECT SUBSTRING_INDEX(a-b-c, -, -1)看结果一目了然。最后再分享一个小技巧MySQL 8.0 之后的版本函数名和关键字的大小写不敏感但表名在 Linux 环境下区分大小写。写 SQL 时可以统一小写养成固定风格团队协作时也能减少不必要的口角。函数是一座桥连接的是“原始数据”和“业务结果”。刚开始学的时候会感觉函数太多了记不住但其实核心高频的就那二十来个用熟之后你会发现写 SQL 的过程就是搭积木字符串函数处理格式日期函数对齐时间条件函数做分支聚合函数做汇总组合起来就是报表、看板、数据分析的各种结果。拿一张你手头真实的表把这些函数逐个试一遍比收藏十篇文章都有用。
返回列表