1. 开局先讲点实在的
“【MySQL】第九节—函数”,看到这个标题点进来,多半是已经在照着教程敲到第九节的同学,要么就是项目里写SQL写到怀疑人生,回来补基础的开发者。不管是哪种,这篇内容能帮你把MySQL里最常用的函数系统捋一遍,省得每次用到都要现查手册、翻博客,效率太低。
函数这东西,平时不觉得它重要,真正写业务的时候离不开。字符串拆了拼、拼了拆,日期算来算去,金额四舍五入,统计报表按月份分组汇总,条件判断换个写法让SQL短一半——全是函数的活。更重要的是,同样的效果,用不用函数、用哪个函数,代码的清晰度和执行效率差别很大。这篇就把常碰到的函数分门别类拆开讲,每个都配案例,直接复制改参数就能用。
适合谁看?零基础跟着教程学的可以当系统讲义读,已经写过一阵子SQL的也能查漏补缺,尤其是那些“我记得有这个函数但怎么拼来着”的情况,这篇就是干这个用的。内容我尽量用大白话讲,保留实操细节,不急的话收藏了慢慢看,急的话直接搜你要的函数名。
2. MySQL函数到底是个啥
2.1 函数在SQL里的位置
MySQL函数,说白了就是MySQL内置好的一批加工数据的“工具”。你给它输入一个或几个值,它按固定规则处理完,返回一个结果。SQL里有表达式的地方,基本就能用函数。比如查一张员工表:
SELECT emp_name, UPPER(emp_name) AS name_up FROM employees;这里UPPER()就是把字符串转大写的函数,输入的emp_name是原值,返回的是处理后的值。它不会改表里的数据,只是影响查询结果。函数大多用在SELECT后面做输出处理,也能用在WHERE条件里做筛选,还能用在ORDER BY里做排序规则,一个函数用在哪、怎么用,效果完全不同,这点后面会结合案例讲。
2.2 按用途分几大类
MySQL的函数按干的事不同,一般分成这几类:
| 类别 | 干什么用 | 典型函数举例 |
|---|---|---|
| 字符串函数 | 处理文本,拼接、截取、替换、格式化 | CONCAT、SUBSTRING、REPLACE、LENGTH |
| 数值函数 | 数学计算、四舍五入、取整、绝对值 | ROUND、FLOOR、CEILING、ABS、MOD |
| 日期时间函数 | 取当前时间、格式化日期、加减日期 | NOW、DATE_FORMAT、DATE_ADD、DATEDIFF |
| 条件控制函数 | 写条件逻辑,类似代码里的 if else | IF、IFNULL、CASE WHEN |
| 聚合函数 | 对一组行做统计汇总 | COUNT、SUM、AVG、MAX、MIN |
| 加密/系统函数 | 哈希、用户、版本等辅助信息 | MD5、USER、DATABASE、VERSION |
这篇把前五类展开讲透,最后补一点加密函数和系统函数的实用场景。每一类我都按“常用函数逐个讲 + 案例组合用”的方式来,保证看完就能往自己SQL里套。
3. 字符串函数:SQL里最操心的部分
字符串函数是日常写SQL时接触最多的一类,因为业务数据里文本占大头。用户姓名、地址、商品名称、订单备注,全是字符串,处理起来花样也多。下面逐个介绍高频函数。
3.1 拼接:CONCAT 和 CONCAT_WS
CONCAT把多个字符串接在一起,注意 MySQL 里如果任何一个参数是NULL,整个结果就是NULL,这坑不少人都踩过。比如查客户表要拼“城市+姓名”:
SELECT CONCAT(city, '-', customer_name) AS location_customer FROM customers;如果有客户的城市是NULL,那这条结果整个变成NULL,后续程序里拿去显示就很奇怪。解决办法可以用CONCAT_WS,它的写法是CONCAT_WS(分隔符, 字符串1, 字符串2, ...),好处是它会忽略NULL值,只把非空的用分隔符拼起来:
SELECT CONCAT_WS('-', city, customer_name) AS location_customer FROM customers;city 为空时,结果会直接变成 customer_name,而不是整条变NULL。用哪个,看业务对空值的容忍度,但凡允许空值存在,CONCAT_WS稳妥得多。
3.2 截取和长度:SUBSTRING、LEFT、RIGHT、LENGTH
SUBSTRING(字符串, 从第几位开始, 截几位)是最常用的截取函数。注意 MySQL的位数从 1 开始数,不是从 0。比如从订单编号ORD20250121里取出日期部分的“20250121”:
SELECT order_no, SUBSTRING(order_no, 4, 8) AS order_date_part FROM orders;LEFT(str, n)取左边 n 个字符,RIGHT(str, n)取右边 n 个字符,适合固定格式的前缀后缀提取。比如取手机号后四位:
SELECT phone, RIGHT(phone, 4) AS phone_tail FROM users;LENGTH()返回的是字节数,不是字符数。这点特别容易踩坑,因为一个中文在 UTF-8 编码下占 3 个字节,LENGTH('你好')返回 6,不是 2。想要字符串的字符个数,要用CHAR_LENGTH():
SELECT LENGTH('你好') AS byte_len, CHAR_LENGTH('你好') AS char_len;结果能清楚看到一个6、一个2。凡是做用户名长度校验、输入内容个数限制的,记得用CHAR_LENGTH,不然中文和英文判定对不上,用户体验很别扭。
3.3 替换和大小写:REPLACE、UPPER、LOWER
REPLACE(str, 旧串, 新串)直接做字符串替换,常用于清洗脏数据、屏蔽敏感词。例如商品描述里统一把“包邮”改成“运费0元”:
SELECT product_name, REPLACE(product_desc, '包邮', '运费0元') AS new_desc FROM products;UPPER和LOWER用来转换大小写,做不区分大小写的比较时两个都能配合用。比如邮箱登录场景下,判断用户输入的邮箱和库里的一致时:
SELECT * FROM users WHERE UPPER(email) = UPPER('TestUser@Example.COM');两边都转成大写再比较,避免用户输入时大小写不一致导致匹配不上。不过这属于一种牺牲索引效率的写法,数据量大的时候得另行考虑,这个后面在“常见问题”里再展开。
3.4 去空格:TRIM、LTRIM、RTRIM
文本数据从外部导入时,经常带着意外的空格,有的是开头、有的是结尾。TRIM()去掉两侧空格,LTRIM()只去左边,RTRIM()只去右边。做精确匹配前批量清一遍很有必要:
SELECT TRIM(customer_name) AS cleaned_name FROM customers;还有一个细节,TRIM不只是去空格,它还能指定要去掉的字符,比如去掉字符串两侧的#:
SELECT TRIM(BOTH '#' FROM '##AB#C##') AS cleaned; -- 结果是 AB#C这个写法日常用得少,但遇到某批数据被统一用某个符号包裹、需要批量清洗时,一条SQL就搞定了,效率比拉出来在程序里处理高很多。
3.5 字符串函数组合实战
单个函数用起来简单,真正考验人的是组合。比如用户表里存了证件号,想打码显示中间四位为****,可以这样写:
SELECT id_number, CONCAT( LEFT(id_number, 3), '****', RIGHT(id_number, 4) ) AS masked_id FROM users;先用LEFT拿前3位,再用RIGHT拿后4位,CONCAT中间补上****,属于经典组合。要是证件号长度不固定,就不能用固定的3和4,得配合函数先算长度再动态取中间部分:
SELECT CONCAT( LEFT(id_number, 3), '****', SUBSTRING(id_number, CHAR_LENGTH(id_number) - 3, 4) ) AS masked_id FROM users;这里用CHAR_LENGTH先算总长度,再通过SUBSTRING定位到倒数第4位开始往后截4位,让代码能兼容不同长度的数据,这在真实业务中挺常见。
4. 数值函数和日期时间函数:算数和时间都靠它
4.1 数值函数逐个说
数值函数处理跟数字打交道的场景,最常见的四个是ROUND、CEILING、FLOOR和MOD。
ROUND(数字, 保留小数位数)做四舍五入,比如价格接口算完折扣后统一保留两位:
SELECT original_price, ROUND(original_price * 0.8, 2) AS discounted_price FROM products;CEILING向上取整,就是只要有小数部分就进一位,比如算分页的总页数经常用它。假设每页显示10条,总数53条:
SELECT CEILING(53 / 10) AS total_pages; -- 结果是 6FLOOR向下取整,抛弃小数部分,适合算已经满了的整批数。MOD(a, b)返回 a 除以 b 的余数,最典型的用途是做数据分片或者奇偶行判断:
SELECT user_id FROM users WHERE MOD(user_id, 2) = 0;这条SQL能把偶数user_id的用户筛出来,简单但实用。
另外两个也值得认识:ABS取绝对值,POWER(x, y)求 x 的 y 次方。数值计算和报表统计中偶尔用得到。
4.2 日期时间函数是大头
日期时间函数可以说是实际开发里最绕不开的,因为几乎每张业务表都有时间字段。先从最常用的几个入手。
NOW()返回当前的日期和时间,CURDATE()只返回日期,CURTIME()只返回时间。在插入订单的时候记录当前时间,直接:
INSERT INTO orders (order_no, created_at) VALUES ('ORD20250201', NOW());DATE_FORMAT()是格式化日期的核心,能把日期按任意格式输出。比如把标准日期转成“2025年02月01日”这样的中文格式:
SELECT DATE_FORMAT(NOW(), '%Y年%m月%d日') AS formatted_date;常用的几个格式化符号记一下:%Y四位年份、%y两位年份、%m两位月份、%d两位日期、%H24小时制、%i分钟、%s秒。数据导出来做报表时,DATE_FORMAT能直接在后端SQL里把格式做好,省得程序里再转一遍。
DATE_ADD(日期, INTERVAL 数值 单位)和DATE_SUB做日期的加减,单位可以是 DAY、MONTH、YEAR、HOUR 等。比如算30天之前的日期:
SELECT DATE_SUB(CURDATE(), INTERVAL 30 DAY) AS date_30_days_ago;会员到期日前一天发提醒这种场景也可以直接写进SQL:
SELECT user_id, expire_date FROM vip_users WHERE expire_date = DATE_ADD(CURDATE(), INTERVAL 1 DAY);DATEDIFF(日期1, 日期2)返回两个日期相差的天数,适合算剩余天数、工龄、账龄:
SELECT DATEDIFF('2025-06-01', '2025-01-15') AS days_diff;如果你要算的是实名认证用户里距离生日还有多少天,或者计算读者借书到期日还剩几天,DATEDIFF就是干这个的。
4.3 日期函数的一个性能提醒
在WHERE里对日期字段直接套函数,存在索引失效的风险。比如:
SELECT * FROM orders WHERE DATE_FORMAT(created_at, '%Y-%m-%d') = '2025-01-20';这句逻辑没错,但 MySQL 没法直接用created_at的索引,因为每一行的数据都先被函数转换了一遍才能比。比较常见的优化写法是改成范围查询:
SELECT * FROM orders WHERE created_at >= '2025-01-20 00:00:00' AND created_at < '2025-01-21 00:00:00';范围查询能直接走索引,数据量一大差距非常明显。记住这个原则:能不加函数就不加函数,实在要按日期查,优先用范围条件。
4.4 数值和日期函数组合案例
需求:查每笔订单的结算金额,结算规则是“金额大于100元部分打9折,不足100元不参与折扣”,订单表里记录了下单时间,要输出“年-月-日”格式的日期。
SELECT order_id, order_amount, ROUND( IF(order_amount > 100, order_amount * 0.9, order_amount), 2 ) AS settle_amount, DATE_FORMAT(created_at, '%Y-%m-%d') AS order_date FROM orders;这里把后面要讲的IF函数也提前用上了。你看,一个查询里数值函数、日期函数、条件函数是混着用的,不用割裂开记,组合才能解决真实问题。
5. 条件控制函数:SQL里也能写 if else
5.1 IF 和 IFNULL 的用法
IF(条件, 值1, 值2)结构简单,条件成立返回值1,不成立返回值2。比如统计订单时把支付状态转成中文:
SELECT order_id, IF(pay_status = 1, '已支付', '未支付') AS pay_status_text FROM orders;这个写法在报表输出、后端拼接口字段时很常用。IFNULL(值1, 值2)专门处理NULL值:如果值1是NULL,返回值2,否则返回值1。比如客户地址字段可能为空,展示时想给个默认值“暂无”:
SELECT customer_name, IFNULL(address, '暂无') AS address_text FROM customers;NULL和空字符串不是一回事,IFNULL只处理数据库里的NULL。如果字段本身存的是'',它就原样返回''。这点在判断是否要展示“暂无”时需要留意。
5.2 CASE WHEN 才是真正的多分支
IF适合二选一,多个条件判断时就该用CASE WHEN了。它的基本结构是CASE WHEN 条件1 THEN 结果1 WHEN 条件2 THEN 结果2 ... ELSE 兜底结果 END。比如根据订单金额把订单分为大、中、小单:
SELECT order_id, order_amount, CASE WHEN order_amount >= 10000 THEN '大单' WHEN order_amount >= 1000 THEN '中单' ELSE '小单' END AS order_level FROM orders;注意CASE WHEN的条件是从上到下挨个判断的,只要匹配到一个就停止,不再往后判断。所以写条件的时候,范围大的、数值高的要放在前面,否则会被后面挡住永远匹配不到。上面这个例子里,>= 10000必须放在>= 1000前面,不然大单全被“中单”截走了。
CASE WHEN还有个很有用的场景,就是做行转列。比如一张学生成绩表,有“姓名、科目、分数”三列,想变成“姓名、语文成绩、数学成绩、英语成绩”这种横向展示:
SELECT student_name, MAX(CASE WHEN subject = '语文' THEN score END) AS chinese_score, MAX(CASE WHEN subject = '数学' THEN score END) AS math_score, MAX(CASE WHEN subject = '英语' THEN score END) AS english_score FROM student_scores GROUP BY student_name;这里是CASE WHEN + 聚合函数 + GROUP BY的组合,能把竖表变横表,报表系统里经常用到这个套路。理解它的核心就是:分组后每个学生一行,MAX只是用来在组内把符合条件的分数取出来,条件之外的行的CASE结果是NULL,不影响结果。
5.3 条件函数在统计中的扩展用法
条件控制函数不只是用来显示,还能在聚合统计里当过滤器。统计订单时只算特定类型:
SELECT pay_channel, COUNT(*) AS total_count, SUM(IF(pay_status = 1, order_amount, 0)) AS paid_amount FROM orders GROUP BY pay_channel;这种方式叫“条件聚合”,不用WHERE先过滤,而是直接在聚合过程中按条件取数。好处是可以在同一次查询里同时拿到总额和已支付额,比先查一遍总数、再查一遍已支付数要高效得多。写报表时这个写法利用率非常高。
6. 聚合函数:报表统计的主力军
6.1 五种聚合函数是基本功
COUNT()、SUM()、AVG()、MAX()、MIN()这五个聚合函数,是做统计的基础。它们的特点都是对一组行进行运算,返回单个结果。COUNT(*)统计行数,COUNT(列名)统计该列非空值的个数:
SELECT COUNT(*) AS total_customers, COUNT(phone) AS has_phone_count FROM customers;一个表里如果某些行的phone是NULL,两个结果会不同。这个差异在判断数据完整程度时可以做参考。
SUM求和,注意如果所有值都是NULL,SUM返回NULL而不是 0。写报表时要小心。AVG求平均,它也会忽略NULL行,比如:
SELECT AVG(score) FROM exam_results;假设有三条成绩,一个是NULL,另外两个是 80 和 100,AVG的结果是 90,不是 60。这个坑在统计学生成绩时挺容易忽视的。MAX和MIN取最大最小值,对文本类型也可以用,按字典序排。
6.2 GROUP BY 是聚合的灵魂
聚合函数单独用是对全表统计,跟GROUP BY配合才能按组统计。比如按商品分类统计每个分类的销量:
SELECT category_id, COUNT(*) AS product_count, AVG(price) AS avg_price, MAX(price) AS max_price FROM products GROUP BY category_id;这里有两条规则容易出错。第一条,SELECT里出现的非聚合列,必须出现在GROUP BY里,否则 MySQL 的严格模式下会直接报错。例如上面要选 category_id,那GROUP BY就得有 category_id。第二条,WHERE在分组前过滤,HAVING在分组后过滤。过滤分组后的统计结果要用HAVING:
SELECT category_id, COUNT(*) AS product_count FROM products GROUP BY category_id HAVING product_count > 5;举一个有点晕但很常见的例子:统计各部门的员工人数,只要人数大于10人的部门。这时候HAVING后面用的是统计出来的别名product_count,而不是原始的列名,这个要记住。
6.3 聚合函数配合条件函数做报表
做报表时最经典的组合是“按时间维度分组 + 条件聚合”。比如按月份统计订单总额和已支付金额:
SELECT DATE_FORMAT(created_at, '%Y-%m') AS month, COUNT(*) AS total_orders, SUM(order_amount) AS total_amount, SUM(IF(pay_status = 1, order_amount, 0)) AS paid_amount FROM orders GROUP BY month;月份的分组是靠DATE_FORMAT把时间戳格式化成'YYYY-MM'字符串来实现的。这里再次用到前面讲过的知识点:日期格式化、条件判断、聚合函数、分组,四个放一起,一条SQL就把月度报表的数据全取出来了。
GROUP_CONCAT这个函数也值得单独提一下,它能将一个分组内的多个值拼成一个字符串。比如查每个分类下都有哪些商品名:
SELECT category_id, GROUP_CONCAT(product_name ORDER BY product_id SEPARATOR '、') AS product_names FROM products GROUP BY category_id;结果里每个分类一行的product_names列包含该分类所有商品名,用分隔符拼好。有个限制是默认长度上限为 1024 字节,超了会被截断,需要改参数或者精简分组内容。真实的商品类目页、标签聚合展示,常用这个函数直接把数据拼好返回给前端,能省掉不少程序里循环拼接的笨拙操作。
7. 加密函数与系统函数:不出名但关键时刻能救命
7.1 加密哈希函数
MD5()、SHA1()、SHA2()这几个函数可以对字符串做哈希运算,生成固定长度的哈希串,适用于敏感信息脱敏或密码摘要存储。比如用户注册时把明文密码转成 MD5 摘要再落库:
INSERT INTO users (username, password_hash) VALUES ('newuser', MD5('password123'));有一点必须提醒:MD5 和 SHA1 早已不适合存密码,暴力碰撞成本太低,现在密码存储至少用SHA2加盐,或者干脆在程序里用专门的密码哈希算法。但在数据一致性校验、接口签名等场景里,MD5还是很好用的轻量工具。比如校验两份数据是否一致:
SELECT COUNT(*) FROM temp_import_data WHERE MD5(CONCAT_WS('|', name, phone, address)) IN (...);用哈希函数快速做对比,比逐字段比对效率高,也避免明文数据直接暴露在日志里。
7.2 系统函数小集合
VERSION()返回 MySQL 版本,DATABASE()返回当前库名,USER()返回当前用户,LAST_INSERT_ID()返回最近一次插入操作的自增ID。这些函数在写脚本、做监控、手动排查问题时很实用。
比如做数据迁移脚本时想先确认库表环境:
SELECT VERSION() AS mysql_version, DATABASE() AS current_db, USER() AS current_user;LAST_INSERT_ID()经典用法是在 A 表插入一条记录后,马上用它的自增主键去插入关联的 B 表。注意它跟连接会话绑定,别的会话插入的数据不会影响当前会话返回的值,这点设计得比较安全。
7.3 实际应用中怎么选
加密和系统函数虽然不起眼,但用对场景能让整体方案干净很多。做数据导入校验时用哈希做指纹比对,做多表关联时用LAST_INSERT_ID(),写运维脚本时用DATABASE()防止连错库。掌握这些细节,在排查问题时会大幅提速。
8. 实战综合案例:一张订单表串起所有函数
8.1 模拟一个真实业务表
既然讲了一堆函数,不如放在一个需求里过一遍。假设有这样一张订单表 orders:
CREATE TABLE orders ( order_id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32), customer_name VARCHAR(64), city VARCHAR(32), order_amount DECIMAL(10,2), pay_status TINYINT, -- 1 已支付 0 未支付 created_at DATETIME );业务需求是:按城市统计每个城市的订单量、订单总额、实付总额(已支付才算)、客单价(总额除以订单量)、最新订单时间,只显示订单量超过 10 的城市,结果按订单总额降序排。
8.2 写一条综合 SQL
SELECT city, COUNT(*) AS order_count, ROUND(SUM(order_amount), 2) AS total_amount, SUM(CASE WHEN pay_status = 1 THEN order_amount ELSE 0 END) AS paid_amount, ROUND(SUM(order_amount) / COUNT(*), 2) AS avg_order_value, MAX(created_at) AS last_order_time FROM orders GROUP BY city HAVING order_count > 10 ORDER BY total_amount DESC;这条SQL里的函数不少:COUNT、SUM、ROUND、CASE WHEN、MAX,加上GROUP BY、HAVING、ORDER BY这些子句。拆开分析执行顺序:先根据WHERE(这里没写)过滤出符合条件的行,然后GROUP BY按城市分组,接着对每组做聚合计算,HAVING对分组后的结果过滤,最后ORDER BY排序。理解这个顺序,写复杂SQL会顺手很多。
8.3 跑完结果看什么
执行后重点检查三件事:第一,order_count > 10这个过滤在分组完成后执行,写错位置会导致结果不对;第二,avg_order_value算的是客单价,如果直接用SUM(order_amount) / COUNT(*),未支付订单也算进去了,可能不符合业务口径;第三,MAX(created_at)查最新订单时间,用DATEDIFF(NOW(), MAX(created_at))还能算出这个城市多久没下单了,这种动态指标做运营分析很常见。
把8个函数串进一条 SQL,比单独记函数更能锻炼实际编码能力。面试时候手写这种题,也是展示基本功的机会。
9. 常见问题与避坑
9.1 LENGTH 和 CHAR_LENGTH 搞混
中文场景较多时,LENGTH返回字节数导致你以为字符串超长,实际并没有。凡涉及界面输入长度限制,稳妥的做法是CHAR_LENGTH。
9.2 GROUP BY 的严格模式报错
只勾选非聚合列、忘加进GROUP BY,在 MySQL 默认开启ONLY_FULL_GROUP_BY的设置下会报错。提示里的那句英文看着吓人,其实解决就是两个选择:要么把列加到GROUP BY里,要么用ANY_VALUE()包一下。
9.3 日期列用函数导致索引失效
WHERE DATE(created_at) = '2025-01-01'这种写法,数据量大的时候会导致扫全表。改成created_at >= '2025-01-01 00:00:00' AND created_at < '2025-01-02 00:00:00'即可。
9.4 HAVING 和 WHERE 用混
WHERE在分组前过滤行,HAVING在分组后过滤组,两者能接受的写法也不同。HAVING可以用聚合函数和别名,WHERE不行。
9.5 CONCAT 遇到 NULL 全变 NULL
CONCAT(a, b)里只要有一个NULL,结果串就是NULL。要么提前用IFNULL包一层,要么直接用CONCAT_WS忽略NULL。另外,IFNULL只处理 NULL,不处理空字符串,注意区分。
9.6 函数写太复杂影响性能
一条SQL里堆五六个函数没问题,但如果函数套函数套到三四层,可读性和性能都开始变差。简单过滤能用BETWEEN就别用函数转换,能在程序里算好的参数就直接传参进来。实际开发里,“能用简单SQL就不用复杂函数”是一种克制,但数据量上来之后确实能避免很多麻烦。
9.7 数字转字符串的隐式转换
CONCAT(123, '456')结果是'123456'字符串,没有报错,但如果你想的是数字相加,这就是个陷阱。做类型判断和运算前想清楚字段类型,必要时用CAST(列名 AS SIGNED)或者CONVERT显式转换。遇到“明明结果看着对但类型不对”的诡异问题,第一个检查项就是隐式类型转换。
10. 函数用的顺手,靠的不只是记语法
函数章节基本都是靠练出来的,不是靠背出来的。这一路看下来,真正的高频函数也就二三十个,但组合起来的场景千变万化。我日常工作里的习惯是:能用函数在 SQL 层解决的问题,尽量不带到程序里再做一遍。数据从数据库里出来就已经加工好,接口代码少写很多循环和判断,出错的概率也低不少。比如分页统计、格式化时间、打码脱敏、行转列,这些都能在 SQL 里一条语句完成,清晰又高效。
另一个心得是,SQL 写复杂了不要怕拆。碰到一条超过五行的查询,先把它拆成小段逐个验证,确认结果对了再拼回去。函数本身不难,难的是组合时边界条件没想清楚,比如空值、边界值、条件顺序。
最后分享一个小技巧:MySQL 官方文档其实挺好查的,每个函数都有详细说明、语法示例和返回值类型。遇到含糊的地方直接查官方文档,比翻各种二手博客要快得多,也不容易被过时或错误的内容误导。把常用函数过一遍之后,再复杂的查询也只是这些基础块的组合罢了。