MySQL不看视频 · SSH 实操

第六单元 · 四类内置函数 · 第 13 / 21 节

字符串与数值函数

建本节自己的表 lesson13_func(5 行,故意留 3 处不整齐),用 CONCAT / LENGTH / CHAR_LENGTH / UPPER / LPAD / SUBSTRING / TRIM / REPLACE 处理文字,再用 ROUND / CEIL / FLOOR / TRUNCATE / MOD / RAND 处理数值;看清 NULL 在函数和聚合里的传播、RAND() 不是定值,最后做一个六位验证码。

2 个步骤 · 进度只存本机

约 20 分钟。这一节只做一件事:把“函数”用熟。先记住一条底线——函数只改变这次查询显示出来的结果,不会改动表里存着的值;然后处理一串文字(拼接、补零、截取、替换),再处理小数和余数,最后做一个六位验证码。

SSH / Linux 终端MySQL 终端输出示例(只读)复制按钮只复制命令,不含 $ / mysql> 提示符和输出。

已完成 0 / 2 步

你现在在哪个提示符

应该在 mysql>。断了就先回到学习终端(PowerShell):

SSH / Linux 终端bash
ssh root@8.148.204.152
SSH / Linux 终端bash
mysql-learning

进去以后确认当前库:

MySQL 终端sql
SELECT DATABASE();
输出示例(只读,不要粘贴执行)text不可复制
+------------+
| DATABASE() |
+------------+
| itcast     |
+------------+
1 row in set (0.00 sec)

显示 itcast 就对了。如果是 NULL,执行 USE itcast;——它只切换当前连接用哪个库,不动数据。

第一步:字符串函数:把一列当成文字来加工

先建这一节自己的表。它和课程里的示例表没有关系,只有 5 行,专门用来练函数:

MySQL 终端sql
SHOW TABLES LIKE 'lesson13_func';
输出示例(只读,不要粘贴执行)text不可复制
Empty set (0.00 sec)

Empty set 说明可以建。如果已经有一行,说明你建过了,跳到下面的 SELECT 核对 5 行即可,不要删表重建。

MySQL 终端sql
CREATE TABLE lesson13_func (
  id INT PRIMARY KEY COMMENT '编号',
  name VARCHAR(20) NOT NULL COMMENT '姓名',
  dept_code CHAR(4) COMMENT '部门代号',
  city VARCHAR(20) COMMENT '城市',
  score DECIMAL(4,1) COMMENT '绩效分',
  months TINYINT COMMENT '在职月数'
) COMMENT='本教程第六单元字符串/数值函数练习表';
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 0 rows affected (0.03 sec)
MySQL 终端sql
INSERT INTO lesson13_func (id, name, dept_code, city, score, months) VALUES
  (1, '张伟', 'a01', '北京市', 88.5, 3),
  (2, '李娜', 'b07', '上海市', 92.0, 12),
  (3, '王强', 'c12', '广州市', NULL, 7),
  (4, '赵敏', 'd03', NULL, 79.5, NULL),
  (5, '陈晨', 'A01', '杭州市', 85.0, 5);
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 5 rows affected (0.01 sec)
Records: 5  Duplicates: 0  Warnings: 0
MySQL 终端sql
SELECT id, name, dept_code, city, score, months FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+--------+-----------+-----------+-------+--------+
| id | name   | dept_code | city      | score | months |
+----+--------+-----------+-----------+-------+--------+
|  1 | 张伟   | a01       | 北京市    |  88.5 |      3 |
|  2 | 李娜   | b07       | 上海市    |  92.0 |     12 |
|  3 | 王强   | c12       | 广州市    |  NULL |      7 |
|  4 | 赵敏   | d03       | NULL      |  79.5 |   NULL |
|  5 | 陈晨   | A01       | 杭州市    |  85.0 |      5 |
+----+--------+-----------+-----------+-------+--------+
5 rows in set (0.00 sec)

这 5 行是故意“不整齐”的:部门代号有的大写有的小写,城市后面都带“市”,赵敏没有城市、没有在职月数,王强没有绩效分。后面每个函数解决其中一处不整齐。

拼接:CONCAT 把几段接成一段

MySQL 终端sql
SELECT id, name, dept_code,
       CONCAT(name, '-', dept_code) AS 标签,
       CONCAT(name, '/', city) AS 城市标签
FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+--------+-----------+------------+------------------+
| id | name   | dept_code | 标签       | 城市标签         |
+----+--------+-----------+------------+------------------+
|  1 | 张伟   | a01       | 张伟-a01   | 张伟/北京市      |
|  2 | 李娜   | b07       | 李娜-b07   | 李娜/上海市      |
|  3 | 王强   | c12       | 王强-c12   | 王强/广州市      |
|  4 | 赵敏   | d03       | 赵敏-d03   | NULL             |
|  5 | 陈晨   | A01       | 陈晨-A01   | 陈晨/杭州市      |
+----+--------+-----------+------------+------------------+
5 rows in set (0.00 sec)

前四行的“标签”都接好了;第 4 行赵敏的“城市标签”是 NULL,因为 city 是 NULL。**CONCAT 里只要有一个参数是 NULL,整个结果就是 NULL**——它不会跳过 NULL 接着接,这一点和很多人第一反应相反。

长度:LENGTH 数字节,CHAR_LENGTH 数字符

MySQL 终端sql
SELECT name, LENGTH(name) AS 字节数, CHAR_LENGTH(name) AS 字符数,
       LENGTH(city) AS 城市字节数, CHAR_LENGTH(city) AS 城市字符数
FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+--------+-----------+-----------+-----------------+-----------------+
| name   | 字节数    | 字符数    | 城市字节数      | 城市字符数      |
+--------+-----------+-----------+-----------------+-----------------+
| 张伟   |         6 |         2 |               9 |               3 |
| 李娜   |         6 |         2 |               9 |               3 |
| 王强   |         6 |         2 |               9 |               3 |
| 赵敏   |         6 |         2 |            NULL |            NULL |
| 陈晨   |         6 |         2 |               9 |               3 |
+--------+-----------+-----------+-----------------+-----------------+
5 rows in set (0.00 sec)

张伟 是 2 个汉字,CHAR_LENGTH 给 2,LENGTH 给 6——每个汉字在 utf8mb4 里占 3 个字节。北京市 是 3 个字、9 个字节。想限制“最多几个字”用 CHAR_LENGTH 的思路;想算存储字节才用 LENGTH。赵敏没有城市,两个长度函数都返回 NULL,不是 0。

比较:大小写和重音,在这一列上“相等”吗

MySQL 终端sql
SELECT 'A01' = 'a01' AS 不分大小写, '张伟' = '张伟 ' AS 尾空格相等, 'e' = 'é' AS 重音相等;
输出示例(只读,不要粘贴执行)text不可复制
+-----------------+-----------------+--------------+
| 不分大小写      | 尾空格相等      | 重音相等     |
+-----------------+-----------------+--------------+
|               1 |               0 |            1 |
+-----------------+-----------------+--------------+
1 row in set (0.00 sec)

三个结果分别是 1、0、1。这张表的字符集是 utf8mb4,排序规则是 utf8mb4_0900_ai_ci:ci = case insensitive(不区分大小写),ai = accent insensitive(不区分重音),所以 A01 等于 a01、e 等于 é;而 _0900_ 系列是 NO PAD,尾部空格参与比较,'张伟' 和 '张伟 ' 不相等。这不是“函数”的功劳,是比较规则在起作用——但它解释了为什么后面 UPPER 出来的结果和原来那一行“看起来相等”。

大小写:UPPER 和 LOWER

MySQL 终端sql
SELECT id, dept_code, UPPER(dept_code) AS 大写, LOWER(dept_code) AS 小写
FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+-----------+--------+--------+
| id | dept_code | 大写   | 小写   |
+----+-----------+--------+--------+
|  1 | a01       | A01    | a01    |
|  2 | b07       | B07    | b07    |
|  3 | c12       | C12    | c12    |
|  4 | d03       | D03    | d03    |
|  5 | A01       | A01    | a01    |
+----+-----------+--------+--------+
5 rows in set (0.00 sec)

第 1 行和第 5 行的原值一个 a01、一个 A01,UPPER 之后都是 A01。函数只改显示,不改存储:再 SELECT dept_code 一次,原值还是原来的大小写。

补位:LPAD 左边补,RPAD 右边补

MySQL 终端sql
SELECT id, LPAD(id, 4, '0') AS 四位编号, RPAD(dept_code, 6, '-') AS 补到六位,
       LPAD('12345', 4, '0') AS 超长被截
FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+--------------+--------------+--------------+
| id | 四位编号     | 补到六位     | 超长被截     |
+----+--------------+--------------+--------------+
|  1 | 0001         | a01---       | 1234         |
|  2 | 0002         | b07---       | 1234         |
|  3 | 0003         | c12---       | 1234         |
|  4 | 0004         | d03---       | 1234         |
|  5 | 0005         | A01---       | 1234         |
+----+--------------+--------------+--------------+
5 rows in set (0.01 sec)

LPAD(id, 4, '0') 把 1 补成 0001;RPAD(dept_code, 6, '-') 在右边补到 6 位。注意第三列:LPAD('12345', 4, '0') 不是补,而是截断成 4 位 1234——目标长度比原串短时,LPAD/RPAD 会截断,不会报错。这一点在给编号补零时很容易踩到。

截取:SUBSTRING 从第几个字开始,取几个

MySQL 终端sql
SELECT id, city, SUBSTRING(city, 1, 2) AS 前两字, SUBSTRING(name, 2, 1) AS 姓名第二字
FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+-----------+-----------+-----------------+
| id | city      | 前两字    | 姓名第二字      |
+----+-----------+-----------+-----------------+
|  1 | 北京市    | 北京      | 伟              |
|  2 | 上海市    | 上海      | 娜              |
|  3 | 广州市    | 广州      | 强              |
|  4 | NULL      | NULL      | 敏              |
|  5 | 杭州市    | 杭州      | 晨              |
+----+-----------+-----------+-----------------+
5 rows in set (0.00 sec)

SUBSTRING(city, 1, 2) 从第 1 个字符开始取 2 个,得到“北京”“上海”这样的前两字;SUBSTRING(name, 2, 1) 取姓名第 2 个字。位置从 1 开始数,不是 0,这是 SQL 和很多编程语言不一样的地方。赵敏没有城市,前两字是 NULL。

清洗:TRIM 去首尾空格,REPLACE 换字,INSTR 找位置

MySQL 终端sql
SELECT id, city, REPLACE(city, '市', '') AS 去掉市, INSTR(city, '州') AS 州的位置,
       TRIM('  ab  ') AS 去首尾空格
FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+-----------+-----------+--------------+-----------------+
| id | city      | 去掉市    | 州的位置     | 去首尾空格      |
+----+-----------+-----------+--------------+-----------------+
|  1 | 北京市    | 北京      |            0 | ab              |
|  2 | 上海市    | 上海      |            0 | ab              |
|  3 | 广州市    | 广州      |            2 | ab              |
|  4 | NULL      | NULL      |         NULL | ab              |
|  5 | 杭州市    | 杭州      |            2 | ab              |
+----+-----------+-----------+--------------+-----------------+
5 rows in set (0.00 sec)

REPLACE(city, '市', '') 把“市”换成空串,得到“北京”“上海”;INSTR 返回子串第一次出现的位置,找不到是 0(不是 NULL),赵敏那行因为 city 是 NULL,所以 INSTR 也是 NULL。TRIM 只去掉首尾空格,中间的不会动。

合起来:一条查询生成工号

MySQL 终端sql
SELECT id, CONCAT(LPAD(id, 3, '0'), '-', UPPER(dept_code)) AS 工号, name
FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+---------+--------+
| id | 工号    | name   |
+----+---------+--------+
|  1 | 001-A01 | 张伟   |
|  2 | 002-B07 | 李娜   |
|  3 | 003-C12 | 王强   |
|  4 | 004-D03 | 赵敏   |
|  5 | 005-A01 | 陈晨   |
+----+---------+--------+
5 rows in set (0.00 sec)

001-A01 是三层套出来的:LPAD 补零 → UPPER 统一大写 → CONCAT 用 - 接起来。函数可以嵌套,执行顺序是从最里面往外。

第二步:数值函数:取整、余数与随机

四舍五入:ROUND 的第二参数是保留几位

MySQL 终端sql
SELECT id, score, ROUND(score) AS 取整, ROUND(score / 3, 2) AS 三分之一
FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+-------+--------+--------------+
| id | score | 取整   | 三分之一     |
+----+-------+--------+--------------+
|  1 |  88.5 |     89 |        29.50 |
|  2 |  92.0 |     92 |        30.67 |
|  3 |  NULL |   NULL |         NULL |
|  4 |  79.5 |     80 |        26.50 |
|  5 |  85.0 |     85 |        28.33 |
+----+-------+--------+--------------+
5 rows in set (0.00 sec)

ROUND(score) 等于 ROUND(score, 0),保留 0 位小数;ROUND(score / 3, 2) 保留 2 位。88.5 变成 89、79.5 变成 80。**对 DECIMAL 这种精确值,.5 往绝对值大的方向走,不是“银行家舍入”**(下面 ROUND(-2.5) 会看到 -3);如果换成 DOUBLE 浮点数,MySQL 交给 C 库处理,可能是另一套规则,本表是 DECIMAL(4,1),不涉及这个坑。王强的 score 是 NULL,两列都是 NULL。

向上取整 CEIL,向下取整 FLOOR

MySQL 终端sql
SELECT CEIL(2.1) AS 上取正, CEIL(-2.1) AS 上取负, FLOOR(2.9) AS 下取正,
       FLOOR(-2.9) AS 下取负, CEIL(score) AS 分数上取, FLOOR(score) AS 分数下取
FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+-----------+-----------+-----------+-----------+--------------+--------------+
| 上取正    | 上取负    | 下取正    | 下取负    | 分数上取     | 分数下取     |
+-----------+-----------+-----------+-----------+--------------+--------------+
|         3 |        -2 |         2 |        -3 |           89 |           88 |
|         3 |        -2 |         2 |        -3 |           92 |           92 |
|         3 |        -2 |         2 |        -3 |         NULL |         NULL |
|         3 |        -2 |         2 |        -3 |           80 |           79 |
|         3 |        -2 |         2 |        -3 |           85 |           85 |
+-----------+-----------+-----------+-----------+--------------+--------------+
5 rows in set (0.00 sec)

前四列是固定的:CEIL(2.1)=3、CEIL(-2.1)=-2、FLOOR(2.9)=2、FLOOR(-2.9)=-3。负数方向最容易记反:CEIL 是往数轴右边(更大)取,FLOOR 是往左边(更小)取,不是“去掉小数”。后面两列对 88.5 得到 89 和 88。

ROUND 和 TRUNCATE 的差别

MySQL 终端sql
SELECT ROUND(2.789, 2) AS 四舍五入两位, TRUNCATE(2.789, 2) AS 截断两位,
       ROUND(-2.5) AS 负半数, ROUND(2.5) AS 正半数;
输出示例(只读,不要粘贴执行)text不可复制
+--------------------+--------------+-----------+-----------+
| 四舍五入两位       | 截断两位     | 负半数    | 正半数    |
+--------------------+--------------+-----------+-----------+
|               2.79 |         2.78 |        -3 |         3 |
+--------------------+--------------+-----------+-----------+
1 row in set (0.00 sec)

ROUND(2.789, 2) 是 2.79,TRUNCATE(2.789, 2) 是 2.78——TRUNCATE 直接砍掉后面的位,不看第三位是几。最后两列说明 .5 的处理:ROUND(2.5)=3、ROUND(-2.5)=-3,都是往绝对值大的方向走。

余数:MOD,符号跟着被除数

MySQL 终端sql
SELECT MOD(7, 3) AS 余数, MOD(-7, 3) AS 负数余数, MOD(id, 2) AS 编号奇偶
FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+--------+--------------+--------------+
| 余数   | 负数余数     | 编号奇偶     |
+--------+--------------+--------------+
|      1 |           -1 |            1 |
|      1 |           -1 |            0 |
|      1 |           -1 |            1 |
|      1 |           -1 |            0 |
|      1 |           -1 |            1 |
+--------+--------------+--------------+
5 rows in set (0.00 sec)

MOD(7,3)=1;MOD(-7,3)=-1(不是 2)——余数的符号跟着被除数走。MOD(id,2) 是 1 表示奇数、0 表示偶数,这是最常见的用法。

数值里的 NULL:算着算着就没了

MySQL 终端sql
SELECT id, score, ROUND(score, 0) AS 取整, score * 2 AS 双倍, score + 1 AS 加一
FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+-------+--------+--------+--------+
| id | score | 取整   | 双倍   | 加一   |
+----+-------+--------+--------+--------+
|  1 |  88.5 |     89 |  177.0 |   89.5 |
|  2 |  92.0 |     92 |  184.0 |   93.0 |
|  3 |  NULL |   NULL |   NULL |   NULL |
|  4 |  79.5 |     80 |  159.0 |   80.5 |
|  5 |  85.0 |     85 |  170.0 |   86.0 |
+----+-------+--------+--------+--------+
5 rows in set (0.00 sec)

第 3 行王强的三个结果都是 NULL。**这里用到的 ROUND / CEIL / FLOOR / TRUNCATE / MOD 拿到 NULL 都返回 NULL**,不会把 NULL 当 0;这是这几个函数各自的行为,别推广成“所有数值函数都如此”。这也是为什么下一节的 IFNULL 有用:要显示一个默认值,得显式写出来。

聚合时 NULL 不计入:平均分按几个人算

MySQL 终端sql
SELECT COUNT(*) AS 行数, COUNT(score) AS 有分数, SUM(score) AS 总分,
       ROUND(AVG(score), 2) AS 平均分, ROUND(SUM(score) / COUNT(*), 2) AS 按行数平均
FROM lesson13_func;
输出示例(只读,不要粘贴执行)text不可复制
+--------+-----------+--------+-----------+-----------------+
| 行数   | 有分数    | 总分   | 平均分    | 按行数平均      |
+--------+-----------+--------+-----------+-----------------+
|      5 |         4 |  345.0 |     86.25 |           69.00 |
+--------+-----------+--------+-----------+-----------------+
1 row in set (0.00 sec)

5 行里只有 4 个人有分数,所以 COUNT(score) 是 4。AVG(score) 的分母是 4,得 86.25;SUM(score) / COUNT(*) 的分母是 5,得 69.00。两个数都对,但说的是两件事——报平均数时先确认分母是几个人,这一点第 7 节已经踩过一次。

随机数:RAND() 每次都变,RAND(种子) 才可复现

MySQL 终端sql
SELECT RAND(42) AS 固定种子;
输出示例(只读,不要粘贴执行)text不可复制
+--------------------+
| 固定种子           |
+--------------------+
| 0.6629110161800972 |
+--------------------+
1 row in set (0.01 sec)

带种子 42 时,这条查询每次重建都得到同一个 0.6629…。换成不带参数的 RAND(),每次执行、每一行都可能不同——所以**不要把 RAND() 的输出写进验收标准**,它本来就不是定值。

用它做六位验证码:

MySQL 终端sql
SELECT LPAD(FLOOR(RAND() * 1000000), 6, '0') AS 六位验证码;
输出示例(只读,不要粘贴执行)text不可复制
+-----------------+
| 六位验证码      |
+-----------------+
| 580528          |
+-----------------+
1 row in set (0.00 sec)

你执行会得到另一个六位数,位数和格式一样:RAND() 落在 [0,1),乘 1000000 后 FLOOR 取整得到 0–999999,再用 LPAD 补到 6 位。这里用 FLOOR 而不是 ROUND,是为了保证结果一定落在 0–999999 这 6 位范围里。

常见报错怎么办

看到意思怎么办
1582 Incorrect parameter count函数参数个数不对用 SELECT 单独试一次,核对函数要几个参数
1054 Unknown column列名拼错,或别名用在了不能用的地方DESC lesson13_func 核对列名
说明 刻意写错的例子:LENGTH 多给了一个参数
MySQL 终端sql
SELECT LENGTH(name, 1) FROM lesson13_func LIMIT 1;
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1582 (42000): Incorrect parameter count in the call to native function 'LENGTH'

LENGTH 只收一个参数,给两个就是 1582。不是“语法错误”(那会是 1064),而是函数参数个数不对。

说明 刻意写错的例子:列名打错一个字母
MySQL 终端sql
SELECT LENGTH(nmae) FROM lesson13_func LIMIT 1;
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1054 (42S22): Unknown column 'nmae' in 'field list'

nmae 不是这张表的列,得到 1054。列名拼错时 MySQL 不会猜你想写什么,直接用 DESC 对照。

练习

先自己做,再展开提示、答案和常见错。

**基础 1. 给每个人生成“姓名(大写部门代号)”的标签,例如 张伟(A01)。**

提示 提示

两层函数:先 UPPER(dept_code),再用 CONCAT 把姓名、括号、结果接起来。

参考答案 答案
MySQL 终端sql
SELECT id, CONCAT(name, '(', UPPER(dept_code), ')') AS 标签
FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+-------------+
| id | 标签        |
+----+-------------+
|  1 | 张伟(A01)   |
|  2 | 李娜(B07)   |
|  3 | 王强(C12)   |
|  4 | 赵敏(D03)   |
|  5 | 陈晨(A01)   |
+----+-------------+
5 rows in set (0.00 sec)
说明 常见错

把括号写成中文全角“(”“)”也能显示,但和题目示例的字符不同;另外 CONCAT('a') 这种只给一段的调用是合法的(返回 a),只有 CONCAT() 一个参数都不给才会报 1582(Incorrect parameter count),少给其中一段不会报错。

基础 2. 每个人的姓名各占几个字符、几个字节?

提示 提示

要两个函数:一个数字符,一个数字节。

参考答案 答案
MySQL 终端sql
SELECT name, CHAR_LENGTH(name) AS 字符数, LENGTH(name) AS 字节数
FROM lesson13_func ORDER BY name;
输出示例(只读,不要粘贴执行)text不可复制
+--------+-----------+-----------+
| name   | 字符数    | 字节数    |
+--------+-----------+-----------+
| 张伟   |         2 |         6 |
| 李娜   |         2 |         6 |
| 王强   |         2 |         6 |
| 赵敏   |         2 |         6 |
| 陈晨   |         2 |         6 |
+--------+-----------+-----------+
5 rows in set (0.00 sec)
说明 常见错

只用 LENGTH 会得到 6,误以为姓名有 6 个字。utf8mb4 下一个汉字 3 字节,6 ÷ 3 = 2 个字。

进阶 3. 把编号补成 4 位,前面补 0。

提示 提示

LPAD(值, 总长度, 补什么)。

参考答案 答案
MySQL 终端sql
SELECT id, name, LPAD(id, 4, '0') AS 工号 FROM lesson13_func ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+--------+--------+
| id | name   | 工号   |
+----+--------+--------+
|  1 | 张伟   | 0001   |
|  2 | 李娜   | 0002   |
|  3 | 王强   | 0003   |
|  4 | 赵敏   | 0004   |
|  5 | 陈晨   | 0005   |
+----+--------+--------+
5 rows in set (0.00 sec)
说明 常见错

写成 LPAD(id, '0', 4)——参数顺序反了,第二个参数 '0' 会被当成目标长度 0,结果直接是空串。第二个参数必须是总长度。

进阶 4. 平均分按“有分数的人”算和按“全部 5 人”算,差多少?

提示 提示

分母分别是 COUNT(score) 和 COUNT(*)。

参考答案 答案
MySQL 终端sql
SELECT ROUND(SUM(score) / COUNT(score), 2) AS 按有分数的人平均,
       ROUND(SUM(score) / COUNT(*), 2) AS 按全部人行平均
FROM lesson13_func;
输出示例(只读,不要粘贴执行)text不可复制
+--------------------------+-----------------------+
| 按有分数的人平均         | 按全部人行平均        |
+--------------------------+-----------------------+
|                    86.25 |                 69.00 |
+--------------------------+-----------------------+
1 row in set (0.00 sec)
说明 常见错

以为 AVG(score) 会按 5 个人算。它自动跳过 NULL,分母是 4;要按 5 个人算必须自己写 SUM/COUNT(*),并且清楚那等于把缺考的人当 0 分。

**概念 5. 为什么 RAND(42) 每次结果一样,RAND() 每次不一样?做验证码该用哪个?**

提示 提示

想想“种子”是什么意思:同样的起点,重复同一串步骤,结果会不会一样?

参考答案 答案

RAND(N) 用固定种子 N 启动伪随机序列,同样的种子得到同样的序列,所以可复现;不带参数的 RAND() 每次调用都取新的随机值。验证码要“不可预测”,用不带种子的 RAND();测试和演示要“可复现”,才用固定种子。

说明 常见错

把验证码的 RAND() 结果抄进验收清单,下一次重跑对不上就以为代码坏了;或者反过来,以为 RAND(42) 能当安全随机数——它只是可复现,并不更随机。

本单元验收

  1. SELECT LENGTH('张伟'), CHAR_LENGTH('张伟'); 得到 6 和 2,能解释 6 是字节数。
  2. CONCAT(name, '/', city) 在赵敏那行返回 NULL,能说出原因。
  3. LPAD('12345', 4, '0') 返回 1234,知道这是截断而不是补位。
  4. AVG(score) 是 86.25、SUM(score)/COUNT(*) 是 69.00,能说出两个分母各是几。
  5. 能说出 ROUND(2.5) 与 ROUND(-2.5) 的结果,以及 TRUNCATE 与 ROUND 的差别。

做到这里

做到这里今天可以停。你现在会让文字拼接、补位、截取、替换,也会对小数取整、求余数、算平均,并且知道 NULL 会一路传染、RAND() 不能当定值。

下一节把函数换到日期和“如果……就……”上:算转正日、在职天数,用 IF/CASE 给每个人分档。

来源与边界

本节对应 BV1Kr4y1i7ru 平台 part 27(原标题「27. 基础-函数-字符串函数」)与 part 28(「28. 基础-函数-数值函数」),主题与同版讲义《MySQL-基础篇》3.1 字符串函数(p53–56)、3.2 数值函数(p56–57)对应;讲义为公开镜像,仅作事实核对。无逐字稿,未声称听过讲解。表名 lesson13_func、5 行数据、全部查询与练习均为本教程原创,未复制讲义正文;页面每段输出都在作者隔离的 MySQL 8.0.46 实例上逐条实跑,0.00 sec 是测试机耗时,你的屏幕可能不同。RAND() 验证码与 CURDATE() 一类随时间变化的输出,页面只展示抓取当次的样子,不当作固定答案。