函数就是「输入进、输出出」的黑盒:函数名(参数, …),返回值当一列用。
参数个数看函数,pi() 一个都不要,replace() 要三个。
三个位置都能放:select 里造列、where 里当条件、order by 里排序。
函数还能套函数:round(avg(height), 2)。
但有个代价:WHERE YEAR(birthday) = 2006 把每一行都改了,
索引按原始值排的,算完再去比就用不上。 换成范围比较,结果一样、索引能用。
前五章学的都是「子句」——怎么写条件、怎么分组、怎么排序。 从这一篇起换一套工具:函数。 函数是 SQL 里最像「编程语言」的部分,也是最容易踩「两个引擎行为不同」的地方。 这一章每一篇都会标出「这里和 MySQL 差在哪」。
一、函数的三个要素
函数名、参数、返回值。 写法统一是 函数名(参数, 参数, …),abs(-3.5) 给 3.50、
round(3.14159, 2) 给 3.14、upper('abc') 给 ABC。
返回值能当一列用,所以最常见的位置就是 select 后面。
也能放进 where 和 order by,那里它就当一个值参与比较。
参数个数不是固定的:pi() 零个、abs() 一个、round() 一到两个、replace() 三个。
记不住就去查手册,这不是该硬背的东西。
二、函数能出现在哪三个位置
select 后面:造一列。SELECT ROUND(height / 100, 2) AS m 给「身高换算成米」。
where 里面:当条件。WHERE YEAR(birthday) = 2006 给两个 2006 年出生的。
order by 后面:按算出来的值排。ORDER BY m DESC 就是按换算后的米数排。
这三个位置覆盖了绝大多数用法。 剩下还有 group by 和 having,
原理一样:函数先算出一个值,再拿这个值去干那件事。
三、函数能套函数
SELECT ROUND(AVG(height), 2) ——avg 先算出 168.4416…,round 再截到两位,给 168.44。
这就是「函数套函数」。
套的顺序是从里往外算,跟数学里的括号一样。 套太多层会难读,超过三层就该考虑拆成子查询。
唯一的禁区:聚合函数不能套聚合函数(max(count(*)) 报错)。
但聚合套普通函数、普通函数套聚合都可以。
四、代价:where 里给列包了函数,索引就废了
WHERE YEAR(birthday) = 2006 看着自然,但它把每一行都改了再比。
索引是按 birthday 的原始值排好的,你算完年份再去比,索引不知道自己该查哪儿。
换成范围比较:WHERE birthday >= '2006-01-01' AND birthday < '2007-01-01',
结果一模一样,但 birthday 上真有索引就能用。
这是 SQL 优化的第一条常识:
函数别包在「要查索引的那一列」外面。 左边随便包(YEAR(now())),右边别包。
这一条在第 4 章讲 where 时没提,是因为那时还没函数。
五、怎么查函数:手册比记忆可靠
MySQL 有 HELP 'ROUND',命令行里直接给语法和例子;图形工具里也能右键看文档。
本沙箱没有这套帮助系统。
所以这一章的每个函数都在 Demo 里跑给你看。 看得见的输出,比背下来的语法靠谱。
真正该记住的是「有没有这个函数」,不是它的第三个参数能不能省。
查手册三十秒,硬背记错三天。
六、动手 Demo:从三类各一个,到撞上索引那条红线
Demo 五段:先三类各来一个,再把函数放到 where、select、order by 三个位置,然后套两层。
最后一段是重点:同一个需求两种写法,结果一样,但一种能用索引、一种不能。
import _q46 as Q
c = Q.con()
# 三类各来一个
c.execute("SELECT ABS(-3.5) AS a, ROUND(3.14159, 2) AS b, "
"UPPER('abc') AS c")
# 函数出现在 where 里
c.execute("SELECT name FROM students WHERE YEAR(birthday) = 2006")
# 函数出现在 select 与 order by 里
c.execute("SELECT name, ROUND(height / 100, 2) AS m FROM students "
"ORDER BY m DESC LIMIT 3")
# 函数套函数
c.execute("SELECT ROUND(AVG(height), 2) AS avg_h FROM students")
# 换成范围比较:结果一样,但索引能用
c.execute("SELECT name FROM students "
"WHERE birthday >= '2006-01-01' "
"AND birthday < '2007-01-01'")
实跑输出:
--------------------------------------------------------------------
第二十三课:函数是「输入进、输出出」的黑盒
--------------------------------------------------------------------
三类各来一个:一个输入一个输出
SQL> SELECT ABS(-3.5) AS a, ROUND(3.14159, 2) AS b, UPPER('abc') AS c
a b c
----------------------
3.50 3.14 ABC
(1 行)
**`abs` 是数学、`round` 是数学、`upper` 是字符串**——**参数放括号里,返回值当一列用。**
**函数的写法都一样**:`函数名(参数, 参数, …)`。
**参数个数看函数**,`pi()` 一个都不要,`replace()` 要三个。
三个位置都能放:select、where、order by
SQL> SELECT name FROM students WHERE YEAR(birthday) = 2006
name
-----
陈静
黄磊
(2 行)
**函数出现在 `where` 里,就是「先算再比」**——把生日算出年份,再跟 2006 比。
两个 2006 年出生的都出来了。
SQL> SELECT name, ROUND(height / 100, 2) AS m FROM students ORDER BY m DESC LIMIT 3
name m
--------------
黄磊 1.80
刘洋 1.78
杨帆 1.75
(3 行)
**函数出现在 `select` 里就是造一列,出现在 `order by` 里就是按算出来的值排。**
函数能套函数:一层套一层
SQL> SELECT ROUND(AVG(height), 2) AS avg_h FROM students
avg_h
-------
168.44
(1 行)
**`avg` 先算出 168.4416…,`round` 再把它截到两位。**
**聚合函数和普通函数能套着用,注意别把聚合套进聚合就行。**
坏消息:where 里给列包了函数,索引就废了
**`WHERE YEAR(birthday) = 2006` 看着自然,但它把每一行都改了。**
**索引是按 `birthday` 的原始值排的,你算完年份再去比,索引不知道自己该查哪儿。**
SQL> SELECT name FROM students WHERE birthday >= '2006-01-01' AND birthday < '2007-01-01'
name
-----
陈静
黄磊
(2 行)
**换成范围比较,结果一模一样,但 `birthday` 上真有索引就能用上。**
**这是 SQL 优化的第一条常识:函数别包在「要查索引的那一列」外面。**
函数怎么查:手册比记忆可靠
**MySQL 有 `HELP 'ROUND'`,图形工具里也能右键看文档。** **本沙箱没有这套帮助系统**,
所以这一篇的每个函数都在 Demo 里跑给你看。
**记不住参数个数很正常。** **要紧的是记住「有没有这个函数」,细节查手册**——
而不是硬背 `substring` 的第三个参数省不省。
第一段三个函数。 abs 给 3.50、round 给 3.14、
upper 给 ABC——数学、数学、字符串,写法完全一样。
第二段 where 里。 2006 年出生的两个:陈静、黄磊。
第三段 select 与 order by。 身高换算成米,按米数排,前三名 1.80/1.78/1.75。
第四段套两层。 avg 算出 168.4416…,round 截成 168.44。
第五段换范围。 同样是陈静、黄磊,但这次 birthday 没被函数包住,索引能干活。
30 秒实验
UPPER('abc')与UPPER(name)(name 是列)用法有什么不同?- 把
WHERE YEAR(birthday) = 2006改成LIKE '2006%',能查出同样的两个人吗? ROUND(AVG(height), 2)里如果把round去掉,输出会是什么样?
常见误区
- 在
where里给要查索引的列包函数。YEAR(birthday) = 2006逻辑没错,但索引完全用不上, 几百万行就是全表扫。 - 硬背参数个数。 函数太多,记混了写出来就是错,查手册比试错快。
- 函数套太多层。 三层以上基本没人读得懂,该拆成子查询。
自测三问(点开对答案)
1. 函数的返回值能放在哪几个位置? —— `select`(造列)、`where`(当条件)、`order by`(按值排),还有 `group by` 和 `having`。 2. 为什么 `WHERE YEAR(birthday) = 2006` 用不上索引? —— 索引按 `birthday` 原始值排序,函数把列改了之后索引无法定位。改成范围比较就能用。 3. 聚合函数能套普通函数吗?反过来呢? —— 都能。`round(avg(x), 2)` 合法;`max(count(*))` 这种「聚合套聚合」报错。总览看完,下一篇开始逐个过——先是数学函数,重点在 round 和 truncate 的差别。
- ☐ (本篇完,下一篇 6.2 数学函数)