拓冰建站拓冰建站
首页 / 资讯中心 / 正文

MySQL内置函数实战指南:从字符串处理到数据分析的SQL效率提升

写这篇MySQL内置函数的分享起因是上周帮一个学弟排查一个数据统计的问题他写了一大段业务代码从数据库里取出关联数据再在Java里循环做字符串拼接和日期格式化代码又长又慢优化之后换成数据库函数一条SQL就搞定了。这个经历让我觉得很有必要把MySQL内置函数的玩法系统整理一下。内置函数用得好很多本来要在应用层花十几行代码处理的事在SQL层面一个函数调用就解决了不管你是做后端开发、数据分析还是日常提数这都是一项绕不开的基本功。1. 内容整体设计与思路拆解1.1 为什么数据库函数值得专门花时间学很多程序员在项目里习惯了“数据库只负责存数据所有逻辑都写在代码里”的玩法。这个思路在小型系统里面没什么问题但一旦数据量上来或者查询逻辑复杂了你会发现把所有逻辑都拉到应用层处理性能和开发效率都很吃亏。举个例子你想查一条订单数据的“下单日期是星期几”如果在Java代码里处理你得先把datetime字段查出来再new一个SimpleDateFormat然后做格式化再判断星期几。如果列表里有几百条数据这套循环逻辑就是几百次的重复代码。但MySQL里一行DAYOFWEEK(create_time)就搞定了还能直接在WHERE条件里做筛选比如“只看周一和周五下的单”一条SQL完事。内置函数的价值不在于让你显得技术很炫而在于它把高频使用的数据处理操作封装成了通用能力让数据库在返回结果之前就帮你完成数据的清洗、转换、聚合。这样应用层的代码更短、更清晰网络传输的数据量也更少。1.2 MySQL内置函数的整体分类体系MySQL官方文档里的函数非常多但如果拆开来看日常开发真正高频用到的是这么几类函数类别代表函数典型使用场景字符串函数CONCAT、SUBSTRING、REPLACE、LENGTH、CHAR_LENGTH字符串拼接、截取、替换、长度统计数值函数ROUND、CEIL、FLOOR、ABS、MOD数值计算、取整、求余日期时间函数NOW、DATE_FORMAT、DATEDIFF、DATE_ADD取当前时间、格式化日期、日期加减计算流程控制函数IF、IFNULL、CASE WHEN条件判断、空值处理聚合函数COUNT、SUM、AVG、MAX、MIN、GROUP_CONCAT统计汇总、分组拼串加密函数MD5、SHA2、AES_ENCRYPT密码加密、数据脱敏系统函数VERSION、DATABASE、USER、UUID获取环境信息、生成唯一标识这个分类不是官方文档的目录是实战场景导向的划分。我下面的内容也是按照这个维度来讲而不是照着官方文档一个个函数罗列——罗列式的文档你随时可以查但哪个场景应该用哪个函数这个经验和判断才是真正值钱的东西。1.3 学习内置函数的最佳路径我的建议是不要死记硬背函数列表而是按“场景驱动”的方式掌握。你先想清楚自己在开发中最常遇到的数据处理需求是什么然后针对性去看这个场景里有哪些函数可用。比如你经常做报表那就先啃日期函数和聚合函数的组合玩法你经常处理用户输入的内容那就把字符串函数吃透。每个函数都建议在本地装一个MySQL建个临时表自己敲一遍。光看不练很快就忘了而且MySQL的版本差异会导致某些函数的行为不一样自己动手跑一遍印象才深刻。下文中的所有SQL示例我都建议你亲手在命令行或客户端里跑一遍。2. 字符串函数的核心细节与实操要点2.1 拼接函数CONCAT和CONCAT_WS的差别字符串拼接是日常开发中用到最多的操作之一。CONCAT(str1, str2, ...)会把参数里的内容按顺序拼接成一个字符串。有一个很多人踩过的坑如果任何一个参数为NULLCONCAT的结果就是NULL而不是忽略空值继续拼接。比如你拼接用户的省市区地址如果某个字段是NULL整个地址就变成NULL了。解决方案有两个一是用IFNULL把可能为空的字段包一层二是换用CONCAT_WS(separator, str1, str2, ...)。CONCAT_WS的第一个参数是分隔符它的好处是自动跳过NULL值但数字0不会被跳过这点要留意。-- CONCAT遇到NULL直接返回NULL SELECT CONCAT(广东省, NULL, 深圳市); -- 结果是NULL -- CONCAT_WS自动跳过NULL结果广东省,深圳市 SELECT CONCAT_WS(,, 广东省, NULL, 深圳市); -- 数字0不会被跳过的坑 SELECT CONCAT_WS(,, 数量, 0); -- 结果数量,02.2 截取函数SUBSTRING和LEFT、RIGHT的搭配使用截取字符串的需求很常见比如提取手机号中间四位、截取身份证号的出生日期。SUBSTRING(str, pos, len)从指定位置开始截取指定长度的字符有几点细节要注意。第一MySQL的字符串位置是从1开始数的不是从0开始这和很多编程语言不一样。第二SUBSTRING支持负数位置表示从字符串末尾倒数。第三LEFT和RIGHT是SUBSTRING的语法糖分别从左边和右边截取固定长度的字符。实际应用中这三个函数经常配合着用。比如提取手机号中间四位可以用SUBSTRING(phone, 4, 4)意思是第4位开始截4个字符提取身份证出生日期通常先用RIGHT把最后一位验证码去掉再LEFT取前8位。SELECT SUBSTRING(广东省深圳市南山区, 4, 3); -- 结果深圳市 SELECT SUBSTRING(广东省深圳市南山区, -3, 2); -- 结果南山 SELECT LEFT(MySQL内置函数讲解, 5); -- 结果MySQL SELECT RIGHT(MySQL内置函数讲解, 2); -- 结果讲解2.3 字符串长度函数LENGTH和CHAR_LENGTH的本质区别这个坑几乎每个新手都会踩。查询字符串长度的函数有两个LENGTH(str)和CHAR_LENGTH(str)。如果存的是纯英文字符两个函数结果一样但存了中文之后结果就不同了。LENGTH返回的是字符串的字节数而CHAR_LENGTH返回的是字符数。MySQL的utf8mb4编码下一个中文字符占3个字节所以LENGTH(内置函数)的结果是124个中文×3字节而CHAR_LENGTH(内置函数)的结果是4。明白了这个区别很多问题就能解释通了。之前有人在设计表时把用户名设置为VARCHAR(6)意思是存6个字符但代码里用LENGTH去判断用户名的长度是否合法结果明明是6个“国”字的用户名LENGTH算出18被判定为超长。这种问题就是函数用错了。SELECT LENGTH(内置函数); -- 结果12 SELECT CHAR_LENGTH(内置函数); -- 结果4 SELECT LENGTH(MySQL); -- 结果5 SELECT CHAR_LENGTH(MySQL); -- 结果52.4 替换与去空格REPLACE和TRIM系列函数数据清洗时最常用的就是替换和去空格。REPLACE(str, from_str, to_str)做的是全局替换也就是说只要匹配到from_str就会替换不是只替换第一个匹配项。去空格有三个方向LTRIM只去掉左侧空格RTRIM只去掉右侧空格TRIM同时去掉两侧空格。注意TRIM只能去掉字符串首尾的空格字符串中间的空格它管不了。如果想把字符串中间的多余空格也压缩成单个空格就得用REPLACE把两个连续空格替换成一个多执行几次直到没有连续空格为止。MySQL 8.0以上版本还有一个TRIM(BOTH x FROM str)的扩展用法可以去掉指定的字符比如去掉字符串两侧的星号。这个用法稍冷门但在处理一些特定格式的数据时非常好用。SELECT REPLACE(a-b-c-d, -, ); -- 结果abcd SELECT TRIM( 前后都有空格的字符串 ); -- 去掉首尾空格 SELECT TRIM(BOTH * FROM **重要信息**); -- 结果重要信息3. 数值与日期时间函数的实战用法3.1 取整函数的差异与选择数值计算中最容易搞混的就是几个取整函数。ROUND(x, d)是四舍五入CEIL(x)或CEILING(x)向上取整FLOOR(x)向下取整。这个差异在金额计算里非常关键。举一个电商场景的例子商品单价是9.9元数量是3件按金额计算逻辑如果先算ROUND(9.9 * 3, 0)结果是30四舍五入但用CEIL(9.9 * 3)结果是30用FLOOR(9.9 * 3)结果是29。而如果单价9.9元买了1件ROUND(9.9, 0)是10CEIL(9.9)是10FLOOR(9.9)是9。另一个容易忽略的点是ROUND的第二个参数如果省略默认取整到0位。如果d是负数表示小数点左侧取整比如ROUND(1234.56, -2)的结果是1200。这个负数的用法知道的人不多但在做千位级别的分组汇总时很实用。SELECT ROUND(9.9), CEIL(9.9), FLOOR(9.9); -- 结果10, 10, 9 SELECT ROUND(1234.56, -2); -- 结果12003.2 日期时间获取函数NOW、CURDATE、CURTIME的取舍开发中经常需要获取当前时间MySQL提供了一组函数NOW()返回当前的日期和时间CURDATE()只返回日期CURTIME()只返回时间还有SYSDATE()也是返回日期时间。大多数场景推荐使用NOW()因为它在语句执行时返回一个固定的时间整条SQL的一致性有保障。而SYSDATE()是函数执行到哪个时间点就返回哪个时刻在一条SQL里多次调用可能得到不同的时间值。这个微妙的差异在长查询或复杂存储过程中可能引发问题。如果只需要日期部分用CURDATE()比如统计当天下单数量WHERE order_date CURDATE()就非常直观。如果只需要时间部分比如判断当前时间是否在允许操作的时间窗口内用CURTIME()。SELECT NOW(), CURDATE(), CURTIME(); -- 示例结果2024-06-01 14:23:45, 2024-06-01, 14:23:453.3 日期格式化DATE_FORMAT的格式符详解DATE_FORMAT是使用频率最高的日期函数可以把日期时间转换成任意你想要的字符串格式。它支持非常多的格式符最常用的是这些格式符含义示例%Y四位年份2024%y两位年份24%m两位月份06%c月份1-126%d两位日01%e日1-311%H24小时制小时14%i分钟23%s秒45%W星期几的英文全称Monday%a星期几的英文缩写Mon%M月份的英文全称June组合使用能实现非常多样的输出比如把时间转成2024年06月01日这种人类友好的格式或者转成2024-06这种月份分组键。做报表按月统计时经常用DATE_FORMAT(create_time, %Y-%m)作为GROUP BY的维度。3.4 日期计算的函数组合DATE_ADD、DATEDIFF、TIMESTAMPDIFF日期的加减计算在业务中太常见了。要查“最近7天注册的用户数”本质就是找出注册时间在今天往前推7天之后的所有用户。DATE_ADD(date, INTERVAL expr unit)就是干这个用的。-- 查询最近7天注册的用户 SELECT * FROM users WHERE register_time DATE_ADD(CURDATE(), INTERVAL -7 DAY);DATE_ADD可以加减DAY、MONTH、YEAR、HOUR、MINUTE、SECOND等时间单位。注意写法上INTERVAL后面的单位是单数形式必须写DAY而不是DAYS。计算两个日期之间差多少天用DATEDIFF(date1, date2)结果等于date1减去date2的天数。如果需要更细粒度地计算差值比如两个时间差多少小时、多少分钟就用TIMESTAMPDIFF(unit, datetime1, datetime2)。注意DATEDIFF只算日期不看时间TIMESTAMPDIFF看完整的时间。-- 两个时间相差多少小时 SELECT TIMESTAMPDIFF(HOUR, 2024-06-01 08:00:00, 2024-06-01 20:30:00); -- 结果12不足一小时的部分舍去4. 流程控制与聚合分析函数的进阶用法4.1 IF函数和IFNULL函数的区别IF(expr, val1, val2)是MySQL里的三目运算符如果expr为真返回val1为假返回val2。这个函数在SELECT列表里做字段级判断非常方便。比如查订单时根据状态值直接输出可读文本SELECT order_id, IF(status 1, 待支付, 已支付) AS status_text FROM orders;IFNULL(expr1, expr2)则更专注它只做一件事如果expr1是NULL返回expr2否则返回expr1。很多人在处理可空字段时经常用COALESCE但其实如果只有两个参数IFNULL的语义更直白。COALESCE是标准SQL里的函数支持多个参数返回第一个非NULL的值能力覆盖IFNULL但IFNULL在MySQL里的执行上有时更直观。实际开发里IFNULL最常见的用途是配合聚合函数。比如统计平均分时如果某个班级没有学生AVG返回NULL界面显示就会是空的用IFNULL包一层返回0体验就好很多。SELECT IFNULL(AVG(score), 0) AS avg_score FROM student_scores WHERE class_id 101;4.2 CASE WHEN实现复杂条件判断CASE WHEN是SQL里实现复杂分支逻辑的核心语法它不是严格意义上的函数但在功能上承担了流程控制职责。常见的写法有两种简单CASE表达式和搜索CASE表达式。简单CASE适合做等值判断搜索CASE适合做范围判断。这两种写法在业务中都很常见。尤其是在做数据分箱比如按年龄段分组、按金额大小档位归类时CASE WHEN几乎是唯一解。SELECT order_id, amount, CASE WHEN amount 100 THEN 小额订单 WHEN amount 1000 THEN 中额订单 ELSE 大额订单 END AS order_level FROM orders;CASE WHEN还可以直接嵌套在聚合函数里面实现“按条件统计”的效果。比如统计一个班各科成绩及格人数可以一条SQL搞定SELECT SUM(CASE WHEN math_score 60 THEN 1 ELSE 0 END) AS math_pass, SUM(CASE WHEN english_score 60 THEN 1 ELSE 0 END) AS english_pass FROM student_scores;4.3 聚合函数COUNT、SUM、AVG的隐藏规则聚合函数是数据分析的基石但有几个隐藏规则值得反复强调。COUNT(*)和COUNT(1)没有实质区别都会统计所有行包括NULL值行。但COUNT(column)只统计该列非NULL的行数。这是面试高频考点也是日常开发容易踩的坑。SUM函数在遇到全NULL时返回NULL而不是0AVG自动忽略NULL值计算平均值MAX和MIN也会忽略NULL值。这些规则在做数据报告时必须心里有数否则报表里会出现莫名其妙的空白。GROUP_CONCAT是很多新手没见过但非常实用的聚合函数它能把分组内多个值拼接成一行字符串。比如查一个用户的所有标签用GROUP_CONCAT可以把标签名拼成逗号分隔的一串文本。行转列操作里它也是常客。SELECT user_id, GROUP_CONCAT(tag_name ORDER BY tag_id SEPARATOR 、) FROM user_tags GROUP BY user_id;4.4 利用GROUP_CONCAT实现行列转换GROUP_CONCAT默认的分隔符是逗号可以通过SEPARATOR指定其他分隔符。需要注意的一个参数是group_concat_max_len默认值是1024字节也就是说拼出来的字符串超过1024字节会被截断。查大量数据拼接时容易踩到这个限制遇到就调整系统变量。-- 查看当前长度上限 SHOW VARIABLES LIKE group_concat_max_len; -- 设置更长当前会话生效 SET SESSION group_concat_max_len 1000000;下面是一个基于GROUP_CONCAT做简单行转列的经典例子把每个学生的各科成绩转成一行展示。虽然真正的动态行转列要配合存储过程或拼接SQL实现但静态列数的场景用GROUP_CONCAT加CASE WHEN就够了。SELECT student_name, MAX(CASE WHEN course 语文 THEN score END) AS 语文, MAX(CASE WHEN course 数学 THEN score END) AS 数学, MAX(CASE WHEN course 英语 THEN score END) AS 英语 FROM student_scores GROUP BY student_name;5. 加密、系统函数与空值处理技巧5.1 密码存储与数据加密的MD5和SHA2MySQL内置了MD5和SHA2系列加密函数常用于密码的哈希存储。MD5(str)返回32位的十六进制字符串SHA2(str, hash_length)支持224、256、384和512位。需要注意MD5在安全性上已经不适合单独用于密码存储了因为彩虹表攻击很容易破解普通密码的MD5值。更稳妥的实践是使用加盐salt的方式把一个随机字符串拼接在密码后面再做哈希。不过这个拼接和哈希的过程放在数据库里做还是应用层做要看你团队的架构约定从安全角度推荐在应用层做完整处理Database函数可以作为辅助。另一个容易理解错的应用是数据脱敏。比如日志表里存了手机号想在查询时只显示前3位和后4位可以结合字符串函数实现CONCAT(LEFT(phone, 3), ****, RIGHT(phone, 4))。这种处理就是典型的利用函数在展示层做脱敏。SELECT MD5(abc123); -- 结果e99a18c428cb38d5f260853678922e03 SELECT SHA2(abc123, 256); -- 结果6ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a841180905.2 UUID函数生成全局唯一标识UUID()返回一个36位的字符串格式是8-4-4-4-12的十六进制包含连字符。它是根据时间和机器特征生成的全局唯一标识适合作为分布式环境下的业务主键或者记录消息的唯一标识。但直接拿UUID当表的主键有一个问题它是无序的字符串InnoDB的聚簇索引在插入时会频繁引起页分裂和随机IO性能比自增整数主键差。常见做法是去掉连字符存成CHAR(32)或者把UUID转成BINARY(16)存储也可以单独建一列存UUID作为业务标识表主键仍然用自增整型。SELECT UUID(); -- 示例结果550e8400-e29b-41d4-a716-446655440000 SELECT REPLACE(UUID(), -, ); -- 去掉连字符后的32位字符串5.3 系统信息函数与调试辅助系统信息函数虽然不像字符串或日期函数那样高频但在调试和运维时很实用。VERSION()返回MySQL版本号DATABASE()返回当前数据库名USER()返回当前连接的用户名和主机名CONNECTION_ID()返回当前连接的ID。这些函数在排查问题时非常有用。比如你在写一个长时间运行的存储过程时可以用CONNECTION_ID()来确认当前执行的是哪个会话方便在另一个会话里做监控或终止。在一条SQL里把版本、当前库、当前用户都查出来也算是一个快速诊断环境的小工具。SELECT VERSION(), DATABASE(), USER(); -- 示例结果8.0.36, test_db, userlocalhost5.4 空值处理的最佳实践NULL值的处理是SQL开发里的一个永恒主题。我之前带项目时见过不少因为NULL导致计算结果出错的案例。除了前面提到的IFNULL还要掌握COALESCE和NULLIF。NULLIF(expr1, expr2)的逻辑是如果expr1等于expr2返回NULL否则返回expr1。这个函数最经典的应用是做除法时的防零处理比如计算某个比例的增长率除数为0时结果为NULL再配合IFNULL转成0。-- 计算增长率当上次数值为0时返回0 SELECT IFNULL( (current_value - last_value) / NULLIF(last_value, 0), 0 ) AS growth_rate FROM metrics;另一个容易被忽略的是NULL和空字符串是不一样的。判断一个字段“为空”要区分两种场景如果业务上把空字符串视为无效数据就得WHERE column IS NULL OR column 。如果只判断NULL漏掉了空字符串的情况统计结果就会有偏差。6. 实操过程用一个综合案例打通内置函数的组合使用6.1 场景设定与建表讲完各类函数用一个综合案例来演示函数的组合使用。假设我们有一个用户订单表需求是按月统计每个用户的订单概况输出每个用户的当月下单次数、订单总金额、首单时间、末单时间以及一个“订单活跃度”评级。先建一张简单的订单表作为演示环境CREATE TABLE user_orders ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, amount DECIMAL(10, 2) NOT NULL, order_time DATETIME NOT NULL, remark VARCHAR(255) ); INSERT INTO user_orders (user_id, amount, order_time, remark) VALUES (1, 199.00, 2024-05-01 10:30:00, 会员日下单), (1, 59.90, 2024-05-15 14:20:00, NULL), (1, 299.00, 2024-06-02 09:00:00, 618预售), (2, 89.00, 2024-05-03 11:00:00, 普通购买), (2, 159.00, 2024-06-15 20:15:00, NULL), (3, 399.00, 2024-06-01 08:30:00, 大额订单);这是一个很常见的数据分布同一个用户在不同月份有多笔订单有备注也有空值备注。6.2 用字符串和日期函数做字段处理第一步先处理原始字段。订单时间直接展示不友好备注字段为NULL时需要显示为“无备注”金额需要格式化成统一的样子。此时字符串函数和日期函数上场。SELECT user_id, DATE_FORMAT(order_time, %Y-%m-%d %H:%i) AS order_time_text, CONCAT(¥, FORMAT(amount, 2)) AS amount_text, IFNULL(remark, 无备注) AS remark_text FROM user_orders;FORMAT函数可以把数字格式化为带千位分隔符的字符串FORMAT(199.00, 2)结果是“199.00”。加上¥前缀是为了展示层面的美观但要注意拼接后的字段已经变成字符串类型不能参与后续的数值计算。这也是一个实战中的常见提醒展示和计算要分开处理不要在一个字段上既做格式化又做聚合。6.3 用流程控制函数做业务评级对这个需求我想根据每月的订单情况给出一个评级订单金额累计大于500元的为“高价值”100到500之间的为“普通”低于100的为“低价值”。这个逻辑用CASE WHEN可以很自然地表达出来。还可以把上个月有没有下过单作为一个维度这就用到了LAG窗口函数但MySQL 8.0才支持窗口函数。如果还在用5.7或更老的版本就需要通过自连接实现。这里直接用CASE WHEN做金额分档逻辑清晰而且兼容性好。SELECT user_id, DATE_FORMAT(order_time, %Y-%m) AS order_month, SUM(amount) AS month_amount, COUNT(*) AS order_count, CASE WHEN SUM(amount) 500 THEN 高价值 WHEN SUM(amount) 100 THEN 普通 ELSE 低价值 END AS value_level FROM user_orders GROUP BY user_id, DATE_FORMAT(order_time, %Y-%m) ORDER BY user_id, order_month;这条SQL同时用了日期函数做分组、聚合函数做统计、流程控制函数做评级。GROUP BY后面直接用DATE_FORMAT的表达式意味着查询结果里也必须包含这个表达式或者用别名方式处理。ORDER BY这里用了别名MySQL是允许的这一点比较友好。6.4 聚合与拼串汇总用户备注信息如果产品经理还想看每个用户每个月在订单备注里都写了什么可以用GROUP_CONCAT做汇总。备注为空的跳过非空的拼接在一起。再结合CONCAT做一段可读的汇总文案。SELECT user_id, DATE_FORMAT(order_time, %Y-%m) AS order_month, GROUP_CONCAT( IFNULL(remark, 未填写) ORDER BY order_time SEPARATOR ) AS remark_summary FROM user_orders GROUP BY user_id, DATE_FORMAT(order_time, %Y-%m);这条SQL展示了字符串函数、流程控制函数和聚合函数三层嵌套的组合用法。IFNULL把空备注填成“未填写”ORDER BY在GROUP_CONCAT内部排序SEPARATOR指定用中文分号拼接。嵌套的层次很多但只要理解了每个函数做的事情整条SQL的逻辑还是很好读的。7. 常见问题与排查技巧实录7.1 字符串比较中的大小写问题MySQL在默认的排序规则collation下字符串比较是不区分大小写的所以WHERE name abc可以匹配到名为“ABC”的记录。但有些业务场景需要区分大小写这就需要在SQL里显式声明。方法论上推荐在表设计阶段就明确字段的排序规则。如果必须临时区分大小写可以在查询时给字段加BINARY关键字或者用STRCMP函数做精确比较。STRCMP(str1, str2)如果两个字符串相同返回0第一个小于第二个返回负数否则返回正数。-- 区分大小写比较 SELECT * FROM users WHERE BINARY username Admin; -- 使用STRCMP SELECT STRCMP(abc, abc); -- 结果0 SELECT STRCMP(abc, abd); -- 结果-17.2 日期函数传参格式不正确导致的隐式转换问题很多人在使用STR_TO_DATE或日期比较时直接传字符串进去MySQL做了隐式类型转换多数时候能正常工作但性能可能受到影响。典型的例子是WHERE order_time 2024-06-01当order_time是datetime类型时这个字符串会被转换为日期时间再比较转换本身没问题。真正容易出问题的是用STR_TO_DATE解析字符串时的格式不匹配。STR_TO_DATE要求格式符与实际字符串严格对应比如字符串是“2024/06/01”格式必须写成%Y/%m/%d如果写成年月日加连字符的格式解析就会失败返回NULL。SELECT STR_TO_DATE(2024/06/01, %Y/%m/%d); -- 正常返回2024-06-01 SELECT STR_TO_DATE(2024/06/01, %Y-%m-%d); -- 结果NULL格式不匹配7.3 隐式类型转换导致的函数行为意外MySQL会自动把不同类型的值做隐式转换这在函数调用时经常会引发一些意想不到的结果。比如字符串和数字比较时MySQL会把字符串开头的数字部分转成数字如果开头不是数字就转成0SELECT 5abc 0; -- 结果5 SELECT abc 0; -- 结果0这个问题在内部函数传参时也同样存在。比如IF函数里如果返回值一个是字符串一个是数字MySQL会根据规则做类型转换导致原本期望的字符串“001”被转成了数字1。遇到这类问题排查思路是先用SELECT单独跑一下函数表达式确认输出类型和值是否符合预期再放进复杂的业务SQL里。7.4 内置函数在索引使用上的注意事项一个重要原则需要反复强调在WHERE条件里对索引列使用内置函数通常会导致索引失效。比如WHERE DATE(create_time) CURDATE()这种写法看似很优雅但MySQL在大多数情况下无法直接使用create_time上的索引因为它需要先对每行的create_time做DATE函数计算再进行等值比较。优化方式是把函数写在条件值那侧而不是写在索引列上。上面的查询可以改写成范围查询-- 不推荐对索引列使用函数 SELECT * FROM orders WHERE DATE(create_time) CURDATE(); -- 推荐使用范围条件 SELECT * FROM orders WHERE create_time CURDATE() AND create_time DATE_ADD(CURDATE(), INTERVAL 1 DAY);如果确实需要经常按日期维度查询另一个方案是额外冗余一个日期字段或使用生成列MySQL 5.7及以上支持让查询可以走索引。这两种方案在实际项目中我都用过根据业务场景选一个就好。7.5 排查函数问题的通用套路最后分享一个排查函数相关问题的通用方法。遇到函数结果和预期不一致时不要直接在大SQL里反复试错而是单独把函数表达式拿出来在最简环境下验证。-- 第一步单独验证函数返回值 SELECT DATE_FORMAT(NOW(), %Y-%m-%d); -- 第二步验证嵌套组合 SELECT STR_TO_DATE(DATE_FORMAT(NOW(), %Y-%m-%d), %Y-%m-%d); -- 第三步再放回业务SQL中验证这个套路看起来基础但效率极高。很多复杂问题其实是多个函数组合在一起后数据类型变化引起的连锁反应单独验证每个函数都能跑对组合起来却错了。一层层拆开跑一遍问题基本就暴露了。MySQL内置函数这个知识面看似散其实非常有逻辑。先把字符串、数值、日期这三类高频函数吃透再掌握流程控制和聚合函数的组合用法最后在前面加一点加密、系统函数的了解日常开发中90%以上的数据处理场景都是可以覆盖的。纸上得来终觉浅最关键的还是回到你的业务里去用。下次遇到要写循环拼接的代码时停下来想一想这个需求是不是一条SQL函数就能完成的
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门