MySQL常用函数实战指南:从字符串到窗口函数的避坑手册
发布时间:2026/9/25 3:01:03来源:尧图网络
如果说每一行 SQL 都是在和表里的数据对话那函数就是我们最顺手的表达工具。刚开始写 MySQL 的那几年我干过最蠢的事就是把函数当字典背——今天查字符串明天查日期结果同一个统计需求写出过三种风格完全不一样的 SQL还都带着奇奇怪怪的坑。后来被线上环境磨了几轮才慢慢摸清楚哪些函数是日常真的抓起来就用、哪些是看似简单实际到处埋雷的。这篇文章不打算按照官方文档的目录给你罗列一遍所有函数那是手册干的事。我更想按实际开发场景把字符串处理、数值计算、日期时间、条件分支、聚合统计、窗口函数这几类高频函数掰开揉碎配合真实案例讲讲它们怎么用、为什么这么用、用的时候有哪些坑。无论你是刚入门的学生还是写过几年 SQL 想查漏补缺的开发者甚至是在准备面试这篇文章应该都能给你点不一样的东西。1. 字符串函数开发里最常用的一批用错就出大问题字符串处理是日常 SQL 里出现频率最高的一类函数没有之一。你以为的字符串拼接、截取、替换和真实业务里的字符串处理往往不是一回事。我见过太多初级开发者因为没搞懂这几个函数的边界条件把线上数据算错然后甩锅给产品经理的非常典型。1.1 CONCAT 拼接别忽略任何一个 NULL拼接字符串看似简单但 CONCAT 有个很多人没注意的行为只要参与拼接的任意一个字段是 NULL整个结果就是 NULL。这个特性曾经坑了我整整一下午。当时做订单表导出的需求需要把省、市、区、详细地址四个字段拼成完整地址。我以为无非就是CONCAT(province, city, district, detail)四条拼一起结果线上有大量订单的省市区字段缺失导出表格里整整齐齐全是空白。排查了半天才发现是 NULL 传染的问题。解决方案是CONCAT_WS它专门处理分隔符拼接且会自动跳过 NULL 值SELECT CONCAT_WS(-, 2025, NULL, 06); -- 结果2025-06 SELECT CONCAT(2025, NULL, 06); -- 结果NULL所以只要涉及多字段拼接且字段可能为空我默认就用CONCAT_WS。它第一参数是分隔符后面参数是要拼接的内容。这个函数比 CONCAT 多了一个参数但多出来的这个参数能帮你省掉一堆IFNULL嵌套。1.2 SUBSTRING 与 SUBSTRING_INDEX截取的正确姿势截取字符串的场景非常多比如把手机号中间四位打码、从邮箱里取出用户名、从商品编号里拆出分类码。MySQL 里有两个好用的截取函数但它们的逻辑完全不同。SUBSTRING(str, pos, len)是按字符位置截取注意这不是按字节是字符。所以处理中文时没问题别去数数儿。它的起始位置pos还可以传负数表示从尾部倒数开始截取。SUBSTRING_INDEX(str, delim, count)是按分隔符截取count为正数时从左往右数第 N 个分隔符之前的部分为负数时从右往左数。这个函数写拆分逻辑特别顺手。举个例子商品编号 CAT-1001-XL要取中间的数字部分SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(CAT-1001-XL, -, 2), -, -1); -- 1001这种“内层取左边到第二段外层取右边第一段”的双层嵌套是拿中间段的经典写法面试里也经常拿这个当基础题考。1.3 REPLACE 与 LIKE替换和模糊匹配的思路差异REPLACE(str, from_str, to_str)是全局替换不是只替换第一处。比如把电话号码里的 - 全部去掉REPLACE(phone, -, )。这个函数在清洗脏数据时极为常用但要注意它对 NULL 同样是无脑返回 NULL配合IFNULL使用更稳妥。模糊匹配这块真正容易踩坑的是LIKE和通配符的组合。%代表任意多个字符含零个_代表一个字符。很多人以为LIKE %abc%很安全但如果你要匹配的内容里本身带%或_字符这两个符号会被当成通配符导致结果完全不对。比如查订单备注里含 50%折扣 的记录直接写LIKE %50%%会匹配出乱七八糟的东西需要转义SELECT * FROM orders WHERE remark LIKE %50\%% ESCAPE \\;ESCAPE 指定转义字符默认就是反斜杠。这就是那种没出过问题就永远意识不到的细节。1.4 字符串长度LENGTH 和 CHAR_LENGTH 的区别统计用户昵称长度你会用哪个函数这种问题看着基础其实特别容易翻车。LENGTH(str)返回的是字节数CHAR_LENGTH(str)返回的是字符数。在 utf8mb4 编码下一个中文汉字占 3 个字节一个 emoji 占 4 个字节。如果需求是“昵称最多 10 个字符”你应该用CHAR_LENGTH判断否则用 LENGTH 会误伤中文用户。如果需求是“某个字段在存储层不超过 255 字节”你得用 LENGTH 去看存储占用。这俩函数没有谁取代谁完全取决于业务场景。2. 数值函数Rounding、取整、随机数坑其实不少数值函数看起来比字符串简单无非就是四舍五入、绝对值、取整这些但一旦和业务规则挂钩就会瞬间变得微妙。我总结过一句话数值函数没有难懂的只有没想清楚的。2.1 ROUND、TRUNCATE、CEIL、FLOOR 怎么选这四个函数是日常最常用的数值处理工具但它们的语义差异非常关键ROUND(x, d)四舍五入保留 d 位小数。注意它进的是第 d1 位的四舍五入不是银行家舍入。MySQL 的 ROUND 对 0.5 的处理是远离零方向这点和某些语言里“逢五成双”的规则不一样别想当然替换逻辑。TRUNCATE(x, d)直接截断到 d 位小数不做四舍五入。很多金额分账场景如果你不想让第三方平台因为四舍五入产生分账差额用 TRUNCATE 反而更可控。CEIL(x)向上取整返回大于等于 x 的最小整数中文叫“进一法”。计算分页的总页数基本都靠它比如记录数 101 条、每页 20 条页数就是 CEIL(101/20) 6。FLOOR(x)向下取整返回小于等于 x 的最大整数。这里有个实际例子计算订单均摊金额时我们要求分钱精确到分每笔订单分摊红包后尾差统一放到最后一笔。写存储过程或 SQL 计算时前面几笔用 TRUNCATE 截断最后一笔用总红包减前面已分摊金额就不会出现一分钱误差。2.2 POW、SQRT、MOD不常用但关键时刻救命POW(x, y)计算 x 的 y 次方比如计算复利POW(1 rate, months)。SQRT(x)算平方根做距离计算时会用到。MOD(x, y)返回 x 除以 y 的余数这个函数在分表、分库按 ID 取模路由时几乎是必修课。比如订单表按用户 ID 做 10 个分表路由规则就是MOD(user_id, 10)。注意 MySQL 里MOD和%运算符等价但 MOD 能在更复杂的表达式里用得更清晰。底层实现上MOD 对负数处理是结果符号和除数一致这个细节在按模路由时要小心别让算法出现单边倾斜。2.3 RAND 随机数取随机记录的正确姿势RAND()返回 0 到 1 之间的随机浮点数RAND(n)可以指定种子。需求“随机抽 N 条记录”是新手最容易写出慢查询的地方SELECT * FROM users ORDER BY RAND() LIMIT 5;对表很大时ORDER BY RAND() 会全表扫描加文件排序性能惨不忍睹。我之前在百万级用户表上跑过一次直接跑了三秒多。更稳妥的做法是先查出一个随机 ID 范围再取记录-- 先拿到主键范围再随机取 SELECT * FROM users WHERE id (SELECT FLOOR(RAND() * (SELECT MAX(id) FROM users))) ORDER BY id LIMIT 5;当然这个方案在 ID 有空洞的时候可能取不满 5 条需要配合业务补偿。但比 ORDER BY RAND() 的性能好一个数量级。3. 日期时间函数格式化、计算、时区最容易踩的索引坑在这里日期时间处理是另一个大型翻车现场。很多同学写 SQL 时对日期函数随意包一层结果好好的索引死活不走等到线上慢查询报警才发现问题出在函数上。3.1 获取当前时间的三个函数NOW 和 CURDATE 别再混用NOW()返回当前的日期和时间格式YYYY-MM-DD HH:MM:SS。CURDATE()只返回当前日期。CURTIME()只返回当前时间。它们各自有对应的NOW(6)这种带小数秒的用法。一个非常典型的坑是“查询当天创建的订单”写成SELECT * FROM orders WHERE DATE(created_at) CURDATE();这个写法的结果没毛病但性能毛病大了。因为对created_at用了DATE()函数后索引列被包了一层函数MySQL 无法直接使用 created_at 上的索引去范围扫描只能全表扫或者全索引扫。百万行以后这个查询能慢到你怀疑人生。正确的做法是直接用范围条件SELECT * FROM orders WHERE created_at CURDATE() AND created_at CURDATE() INTERVAL 1 DAY;类似的坑还有YEAR(created_at) 2025同样应该改成created_at 2025-01-01 AND created_at 2026-01-01。这是日期时间类 SQL 最重要的性能守则——别在索引字段上包函数。3.2 DATE_FORMAT 格式化输出简单输入要小心DATE_FORMAT(date, format)是把日期输出成指定格式。比如DATE_FORMAT(now(), %Y-%m-%d %H:%i:%s)注意这里的%i代表分钟而不是%m%m是月份。大小写完全不同写错一个字母统计出来的数据就差一年。这种细节只能靠踩坑记忆我一度就是靠文档对照着写的。格式化函数还有个意想不到的用途统计报表里按天、按月分组的输出。比如按小时统计订单量的写法SELECT DATE_FORMAT(created_at, %Y-%m-%d %H:00:00) AS hour_bucket, COUNT(*) AS order_cnt FROM orders GROUP BY hour_bucket;把 created_at 格式化成小时级别的桶再进行分组聚合。但同样注意如果表数据量大这种写法在 GROUP BY 上也可能存在性能问题可以考虑在查询条件里限定一个时间范围窗口别全表跑。3.3 DATEDIFF、TIMESTAMPDIFF、DATE_ADD计算和偏移天数差直接用DATEDIFF(end_date, start_date)它只比较日期部分忽略时分秒。但如果你要精确到小时或秒的差值就得用TIMESTAMPDIFF(unit, start, end)unit 可以是 SECOND、MINUTE、HOUR、DAY 等。很多新手不知道 TIMESTAMPDIFF 是拿第二个参数减第一个参数和 DATEDIFF 的参数顺序刚好相反这个方向性问题我至少被问过三次。日期偏移场景比如“查询 30 天前到现在的数据”用DATE_SUB(now(), INTERVAL 30 DAY)或等价的正数方向DATE_ADD(now(), INTERVAL -30 DAY)。月底最后一天的需求则用LAST_DAY(2025-02-10)返回 2025-02-28。这些函数组合起来月报、周报、滚动窗口统计都能轻松搞定。还有个实用技巧EXTRACT(YEAR_MONTH FROM created_at)可以直接取出202502这种格式配合月份分组统计非常顺而且语义比 DATE_FORMAT 更明确性能也没差。4. 条件判断与逻辑处理IF、CASE WHEN、IFNULL你真的用对了吗条件函数是 SQL 里的“分支语句”很多程序员习惯把业务分支写在应用层但如果数据量不大或者统计需求临时直接在 SQL 里用条件函数反而更高效。这里面的关键点在于函数怎么嵌套、NULL 怎么兜底直接影响正确性和可读性。4.1 IF 函数简洁但别过度嵌套IF(expr, true_value, false_value)是 MySQL 里最简单的条件判断。比如按照状态标记订单是否超时SELECT order_id, IF(status pending AND created_at NOW() - INTERVAL 2 HOUR, 超时, 正常) AS flag FROM orders;但不要为了追求一行 SQL 写多个 IF 嵌套真的非常难维护。我见过有人写IF(a, IF(b, IF(c, 1, 2), 3), 4)这种三层嵌套后来需求改了一次所有人都看不懂那段逻辑表达的是什么业务含义。这种情况老老实实换CASE WHEN。4.2 CASE WHEN这才是真正的分支利器CASE WHEN 有两种语法简单 CASE 表达式和搜索 CASE 表达式。搜索式更常用SELECT CASE WHEN total_amount 1000 THEN 大额订单 WHEN total_amount 100 THEN 中额订单 ELSE 小额订单 END AS order_level, COUNT(*) AS cnt FROM orders GROUP BY order_level;注意 CASE WHEN 是顺序匹配的写条件时要把大范围放前面小范围放后面否则小条件永远先命中统计结果就会不对。上面例子中如果先写total_amount 100那么 1000 元以上的订单都会被归到中额订单。另外 CASE WHEN 可以直接嵌在聚合函数里实现“有条件地计数”。比如统计每个用户 7 月下单次数和 8 月下单次数一张表直接搞定SELECT user_id, COUNT(CASE WHEN month(created_at)7 THEN 1 END) AS july_cnt, COUNT(CASE WHEN month(created_at)8 THEN 1 END) AS aug_cnt FROM orders WHERE created_at 2025-07-01 AND created_at 2025-09-01 GROUP BY user_id;这种写法比自连接、子查询都更直观性能通常也更好。它利用 COUNT 只数非 NULL 的特性CASE WHEN 没命中的返回 NULL 不计数命中一个就数一个。类似的还可以用 SUM(CASE WHEN ... THEN 1 ELSE 0 END) 实现效果一致。4.3 IFNULL、NULLIF、COALESCENULL 处理的三个层次IFNULL(expr1, expr2)是处理 NULL 的第一道防线如果 expr1 是 NULL 就返回 expr2。COALESCE(expr1, expr2, expr3, ...)更灵活从左到右返回第一个非 NULL 值。这两者的选择其实很简单两个参数用 IFNULL多个参数用 COALESCE。真正容易忽略的是NULLIF(expr1, expr2)它的逻辑是如果两个表达式相等就返回 NULL否则返回 expr1。这个函数做“防止除零”很有意思SELECT total_amount / NULLIF(quantity, 0) AS unit_price FROM orders;当 quantity 为 0 时NULLIF 返回 NULL整个除法结果为 NULL而不是报错或者出现无穷大。这个函数在报表里处理除零场景非常实用比 CASE WHEN 写得简洁。我自己的习惯是所有可能为空的字段进入计算型 SQL 之前先统一处理一遍 NULL宁可在源头堵住也不要在后来每个公式里 IFNULL 一次。比如建临时表时直接把可能有 NULL 的列转为 0 或上一步的空字符串标记。5. 聚合函数与分组统计COUNT 的坑、GROUP_CONCAT 的默认限制聚合函数是数据库的看家本领但越基础的东西越有讲究。COUNT 的三种写法、SUM 对 NULL 的处理、GROUP BY 的隐式陷阱这些如果没搞清楚报表数据你怎么算怎么不对劲。5.1 COUNT(*)、COUNT(1)、COUNT(字段)到底有什么区别这个题几乎是面试必问。简单说COUNT(*)统计的是行数包括那些所有列都为 NULL 的行。它不会去逐列判断直接按行统计。COUNT(1)和 COUNT(*) 在 MySQL InnoDB 引擎下结果一样也统计所有行。1 在这里只是一个常量表达式不代表什么特殊含义。COUNT(字段)只统计该字段为非 NULL 的行数。如果这个字段大量为 NULLCOUNT 出来的数字会比实际行数少。有个真实案例统计“已发货订单数”时用了COUNT(ship_time)结果线上订单表里很多历史订单的 ship_time 字段是空的月底一核算发现发货率只有 80%实际上业务那边发货是 100%。问题就出在 COUNT 字段会跳过 NULL 行而不是字段值本身有错。5.2 SUM 和 AVG 的 NULL 传染SUM 计算时也会自动跳过 NULL 值但有个不一样的点如果所有值都是 NULLSUM 返回 NULL 而不是 0。所以对金额字段做汇总时如果直接展示到报表前端页面很容易显示空白而不是 0。建议对可能全为 NULL 的字段包一层 IFNULLSELECT IFNULL(SUM(amount), 0) AS total_amount FROM orders;AVG 同样跳过 NULL但这样做有时会产生误导。比如五个样本里有三个 NULLAVG 只对两个非 NULL 值取平均。如果你希望 NULL 当作 0 参与均值计算应该先把字段转成 0AVG(IFNULL(score, 0))。这两种写法的业务含义完全不一样上线之前一定要和产品确认清楚。5.3 GROUP BY 的三个容易忽视的坑第一GROUP BY后面的字段和 SELECT 里的非聚合字段必须一致否则在开启 ONLY_FULL_GROUP_BY 模式时会直接报错。MySQL 5.7 以后默认开启这个模式很多老项目升级后就各种 SQL 报错根因都是当年写 SQL 时太随意。不要把“能跑就行”当习惯SQL 模式本身就在帮你规范写法。第二GROUP BY 和 WHERE 的执行顺序。WHERE 是在分组之前过滤行HAVING 是在分组之后过滤组。也就是说你不能在 WHERE 里写聚合条件比如WHERE COUNT(*) 10一定会报错。必须写成SELECT user_id, COUNT(*) AS cnt FROM orders GROUP BY user_id HAVING cnt 10;第三对 NULL 列的 GROUP BY所有 NULL 值会被分到同一组。有些业务场景需要和“未知”放在一起有些场景你希望过滤掉。如果不想让 NULL 参与分组可以在 GROUP BY 前用 WHERE 排除或者在 SELECT 里用 COALESCE 先转换默认值。5.4 GROUP_CONCAT小心默认长度限制GROUP_CONCAT是分组内把字段值拼接成一个字符串。比如查询一个用户的所有订单号SELECT user_id, GROUP_CONCAT(order_id ORDER BY created_at SEPARATOR ,) FROM orders GROUP BY user_id;这个函数有个非常隐蔽的坑默认最大长度是 1024 字节超长部分会被静默截断。等你发现导出的数据少了订单号排查上来花了不少时间。解决办法是在会话级或者全局设置 group_concat_max_lenSET SESSION group_concat_max_len 10240;另外尽量用 ORDER BY 子句控制拼接顺序否则 GROUP_CONCAT 的顺序是不确定的同一份数据多次查询出来的拼接顺序可能不一样这对下游解析程序来说是个稳定性隐患。6. 窗口函数排名、累计、分区内计算SQL 进阶必备MySQL 8.0 开始支持窗口函数这是我从老版本迁移后最爽的升级点。窗口函数的本质是“不改变行数的同时在行组内做计算”。和 GROUP BY 不同窗口查询的每一行都保留而不是折叠成一行。这个特性让它非常适合解决“分组内排名”“同比环比”“累计求和”这类需求。6.1 ROW_NUMBER、RANK、DENSE_RANK 三者怎么区分排名函数是窗口函数里最常见的但三个函数的行为差异经常有人记混ROW_NUMBER()每一行一个唯一的序号即使并列排名也会强行走一个先后1、2、3、4。RANK()相同值并列排名并列后跳过名次。比如两个并列第一结果是 1、1、3。DENSE_RANK()相同值并列排名但连续不跳号。两个并列第一结果 1、1、2。举一个实际例子。查找每个部门薪水最高的员工用 ROW_NUMBER 取每个分组内第一条SELECT employee_id, dept_id, salary FROM ( SELECT employee_id, dept_id, salary, ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn FROM employees ) t WHERE rn 1;如果是“找出每个部门薪水前两名且允许并列”那就要用 RANK 或 DENSE_RANK 再去过滤。理解了三者的差异写这类业务就不会拿到错误结果。6.2 累计求和的经典写法累计求和running total用窗口函数写起来非常优雅。比如统计订单表计算每个月的累计订单金额SELECT DATE_FORMAT(created_at, %Y-%m) AS month, SUM(amount) AS month_amount, SUM(SUM(amount)) OVER (ORDER BY DATE_FORMAT(created_at, %Y-%m)) AS cumulative_amount FROM orders GROUP BY DATE_FORMAT(created_at, %Y-%m);这里的窗口OVER (ORDER BY 月份)没写 PARTITION BY表示对整个结果集按月份排序并逐步累加。注意窗口 ORDER BY 的排序逻辑必须和显示顺序一致否则累计值看起来会莫名其妙地“跳变”。6.3 LEAD 和 LAG环比和同比环比计算用LAG(字段, N)取分组内上一行的值LEAD取下一行的值。比如计算每个月的订单金额环比增长率SELECT month, amount, LAG(amount, 1) OVER (ORDER BY month) AS prev_amount, (amount - LAG(amount, 1) OVER (ORDER BY month)) / LAG(amount, 1) OVER (ORDER BY month) AS growth_rate FROM monthly_summary;用窗口函数做环比不用写自连接不用写子查询可读性高了一个档次。但要注意 LAG 在边界行会返回 NULL处理增长率的除零问题前先想清楚边界怎么兜底。窗口函数也并不是完全没有性能代价。它会在内存或临时表里维护窗口数据如果数据量极大且窗口很宽内存压力甚至会超过 GROUP BY。所以窗口函数适合“计算结果行数和明细行一致”的场景如果最终要的是汇总一行还是老老实实 GROUP BY 更稳。7. 排序相关的隐形细节ORDER BY 的坑和排序规则很多人觉得排序有什么好说的不就是 ORDER BY 加字段。但实际上排序相关的坑特别容易出现在“字段类型”和“编码规则”上。热搜里专门有 mysql排序 这个词说明这是很多人实操时绕不过去的点。7.1 ORDER BY 中文排序和大小写问题MySQL 默认排序规则和字符集有关。UTF-8 下默认的排序规则通常是utf8mb4_0900_ai_ciMySQL 8.0或utf8mb4_general_ci老版本这个规则对中文是按 Unicode 编码排序的不是按拼音也不是按笔画。想要按拼音排序需要指定排序规则SELECT name FROM users ORDER BY name COLLATE utf8mb4_zh_0900_as_cs;不过说实话如果排序需求真是“按拼音排”我建议放在应用层处理用数据库排序规则硬顶中文拼音排序会有各种边界问题尤其是多音字即便数据库规则号称支持效果也未必符合产品预期。数据库排序强项在字母、数字和常规字符集中文人名拼音真的不适合在这里硬搞。7.2 ORDER BY 与 NULL 的默认位置ORDER BY column ASC时MySQL 默认把 NULL 排在前面ORDER BY column DESC时NULL 排在后面。如果你希望“空值永远在最后”需要额外处理SELECT * FROM users ORDER BY ISNULL(age), age ASC;ISNULL(age)作为一个布尔表达式如果 age 是 NULL 返回 1否则返回 0排序时 0 总是在 1 前面ASC 下这样就实现了空值置底。这个写法比ORDER BY IFNULL(age, 999999) ASC干净得多。7.3 ORDER BY 与 LIMIT 组合优化ORDER BY和LIMIT一起用时MySQL 可能会走 Top-N 优化只排序输出前 N 行的数据而不用把整个结果集都排完这能省大量 IO。但要触发这个优化ORDER BY 上的字段最好能对应用上索引。无法用索引时MySQL 会先生成排序文件再取前 N 行表一大就会慢。我之前调过一个按月报表的查询ORDER BY 了三个字段但索引只覆盖了前两个结果 MySQL 对第三字段做了文件排序数据量两百万行时继续等不出来。后来调整了查询语句把第三个字段也纳入关联索引查询时间从 7 秒降到了 0.2 秒。排序字段的性能影响往往不是字段本身复杂而是索引设计没有跟着排序逻辑走。8. 面试里那些高频函数题换个姿势准备MySQL 函数和面试题结合得非常紧密热搜词里也不少。这里挑几个最有代表性的直接给出我的思考路径比背标准答案有用得多。8.1 一道经典的“取每个分类最新一条记录”老写法是用子查询找最大时间SELECT * FROM products p WHERE p.created_at ( SELECT MAX(created_at) FROM products WHERE category_id p.category_id );这个写法在分类多、表大时会执行多次子查询性能不太行。窗口函数版本更清爽SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY created_at DESC) AS rn FROM products ) t WHERE rn 1;这个才是 MySQL 8.0 之后我认为的最优解。如果面试官再追问 ROW_NUMBER 和 RANK 的差异正好把前面 6.1 小节的点说出来会显得你理解很扎实。8.2 字符串相关的常见笔试题笔试题里很爱考“把逗号分隔的字符串拆成多行”。比如 a,b,c,d 拆成四行数据。传统做法是借助数字辅助表利用SUBSTRING_INDEX循环取段。MySQL 8.0 以上可以用 JSON_TABLE 或者递归 CTE但为了兼容性和通用性我通常还是会写基于数字辅助表和 SUBSTRING_INDEX 的版本SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(a,b,c,d, ,, n.num), ,, -1) AS value FROM ( SELECT row : row 1 AS num FROM (SELECT row : 0) r, information_schema.COLUMNS LIMIT 4 ) n;这个写法本质是“借助行号截取每一段”笔试面试里能写出来基本就过关了。如果你打算在实际项目里用建议确认辅助表的数据量别太大否则可能直接把 information_schema 这张元数据表拖累成性能瓶颈。8.3 关于函数“能不能用索引”的面试追问这是函数题里最可能考到深度的地方。面试官问“你对 created_at 用了 DATE() 函数后为什么索引会失效”你需要回答的关键点是索引树里存储的是原始字段值不是函数计算后的值。当查询条件把索引列包在函数里时优化器无法直接根据函数结果去查找索引树中对应的有序区间所以只能放弃索引转为全表扫描。然后一定要补一句上面的解决方案是改写为范围条件。如果条件本身就离不开函数可以考虑在 MySQL 8.0 里建函数索引InnoDB 支持表达式索引或者新建一列专门存函数计算结果单独加索引。这几种方案各有适用场景面试时能说出来就是加分项。写在最后我的一点实操经验看了这么多函数最重要的其实不是背下每个函数的参数列表而是培养一种条件反射——拿到一个需求先判断属于哪类场景再想想哪几个函数能组合出方案最后在心里过一遍边界条件。NULL 怎么办、空字符串怎么办、超长怎么办、类型不匹配怎么办这些比函数本身的语法更容易在生产环境里出问题。我个人有一个习惯所有常用的函数组合我会单独建一个 SQL 笔记文件把自己踩过的坑、验证过的写法、性能对比结果都记在里面。下次写类似需求的 SQL 时直接翻笔记而不是重新百度。日积月累这些笔记比任何文档都实用。如果这篇文章能帮你少踩几个坑或者让你在面试时多答上来一道函数题那就不白写。数据库函数的世界其实不复杂复杂的是业务边界和底层性能约束。多写、多验证、多回头看自己写过的 SQL慢慢就会形成手感。
网站建设高端定制企业官网