课程 › 第 15 章 › 实验三
第 15 章 · 实验三 · 在第五节之后

常用内置函数

在 teaching 库的表上使用数学、字符串、日期、系统信息、加密、聚合和控制流函数,每类挑常用的几个。

环 境

MySQL 8.0 和 MySQL Workbench,机房电脑已装好。所有语句在 Workbench 的 SQL 编辑区中执行。

执 行

Ctrl+Enter 执行光标所在的一句,Ctrl+Shift+Enter 执行全部。Mac 上把 Ctrl 换成 ⌘。

注 意

输入法切到英文。表名、字段名不加引号,字符串值才用单引号。

本次实验的顺序 后一个实验用到前一个的结果

  1. 实验 3-1数学函数
  2. 实验 3-2字符串函数
  3. 实验 3-3日期和时间函数
  4. 实验 3-4系统信息与加密函数
  5. 实验 3-5聚合函数
  6. 实验 3-6控制流与类型转换函数
第一步

数据准备(必做)

重建 teaching 库的五张表和教材中的数据,并删除本章实验建立的存储过程和函数。

整段复制,全部执行下载 .sql
-- 第 15 章实验数据:教材 teaching 库的五张表和教材中的数据
-- 整段执行一次即可。五次实验共用,想从头再做时重新执行。
CREATE DATABASE IF NOT EXISTS teaching DEFAULT CHARACTER SET utf8mb4;
USE teaching;

-- 删除本章实验建立的存储过程和函数
DROP PROCEDURE IF EXISTS s_count;
DROP PROCEDURE IF EXISTS info_count;
DROP PROCEDURE IF EXISTS pass_or_fail;
DROP PROCEDURE IF EXISTS sum100;
DROP PROCEDURE IF EXISTS max_i;
DROP PROCEDURE IF EXISTS odd_sum;
DROP PROCEDURE IF EXISTS showstudentsnums;
DROP PROCEDURE IF EXISTS cal_courses;
DROP PROCEDURE IF EXISTS dept_students;
DROP PROCEDURE IF EXISTS cal_students;
DROP PROCEDURE IF EXISTS double_it;
DROP PROCEDURE IF EXISTS fetch3;
DROP PROCEDURE IF EXISTS dept_names;
DROP FUNCTION IF EXISTS cal_depts;
DROP FUNCTION IF EXISTS isprime;
DROP FUNCTION IF EXISTS get_level;

-- 删除旧的表。先暂时关闭外键检查,库里有别的表引用它们时也能删除
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS sc, tc, avgsal, classroom, s, c, t;
SET FOREIGN_KEY_CHECKS = 1;

-- 课程表
CREATE TABLE c (
  cno CHAR(4) NOT NULL,
  cn VARCHAR(30) NOT NULL,
  ct INT,
  PRIMARY KEY (cno)
);
-- 学生表
CREATE TABLE s (
  sno CHAR(4) NOT NULL,
  sn VARCHAR(20) NOT NULL,
  sex CHAR(2),
  age INT,
  maj VARCHAR(30),
  dept VARCHAR(30),
  PRIMARY KEY (sno)
);
-- 教师表
CREATE TABLE t (
  tno CHAR(4) NOT NULL,
  tn VARCHAR(20) NOT NULL,
  sex CHAR(2),
  age INT,
  prof VARCHAR(20),
  maj VARCHAR(30),
  dept VARCHAR(30),
  sal DECIMAL(8,2),
  PRIMARY KEY (tno)
);
-- 选课表
CREATE TABLE sc (
  sno CHAR(4) NOT NULL,
  cno CHAR(4) NOT NULL,
  score DECIMAL(5,2),
  PRIMARY KEY (sno, cno),
  FOREIGN KEY (sno) REFERENCES s (sno),
  FOREIGN KEY (cno) REFERENCES c (cno)
);
-- 授课表
CREATE TABLE tc (
  tno CHAR(4) NOT NULL,
  cno CHAR(4) NOT NULL,
  tcdate DATE,
  PRIMARY KEY (tno, cno),
  FOREIGN KEY (tno) REFERENCES t (tno),
  FOREIGN KEY (cno) REFERENCES c (cno)
);

INSERT INTO c VALUES
('c1','Java程序设计',40), ('c2','程序设计基础',48), ('c3','线性代数',48),
('c4','数据结构',64), ('c5','数据库系统',56), ('c6','数据挖掘',32),
('c7','高等数学',60), ('c8','控制理论',32);

INSERT INTO s VALUES
('s1','王彤','女',18,'计算机','信息学院'),
('s2','苏乐','男',20,'信息','信息学院'),
('s3','林欣怡','女',19,'信息','信息学院'),
('s4','陶然','男',19,'自动化','工学院'),
('s5','魏立','男',17,'数学','理学院'),
('s6','何欣荣','女',21,'计算机','信息学院'),
('s7','赵琳琳','女',19,'数学','理学院'),
('s8','李轩','男',18,'自动化','工学院');

INSERT INTO t VALUES
('t1','刘杨','男',38,'教授','计算机','信息学院',3800.00),
('t2','石丽','女',28,'讲师','计算机','信息学院',2800.00),
('t3','顾伟','男',35,'副教授','计算机','信息学院',3200.00),
('t4','赵礼','男',45,'教授','自动化','工学院',4267.90),
('t5','赵希希','女',33,'副教授','数学','理学院',3332.67),
('t6','张刚','男',30,'讲师','计算机','工学院',2600.00);

INSERT INTO sc VALUES
('s1','c1',90.50), ('s1','c2',88.00),
('s2','c4',70.00), ('s2','c5',57.00), ('s2','c6',81.50), ('s2','c7',NULL),
('s3','c1',75.00), ('s3','c2',76.50), ('s3','c4',85.00),
('s4','c1',93.00), ('s4','c2',82.00), ('s4','c6',NULL), ('s4','c7',68.00),
('s5','c2',79.00), ('s5','c7',84.00),
('s7','c3',77.00), ('s7','c5',62.00), ('s7','c7',100.00),
('s8','c2',66.00), ('s8','c3',96.00);

INSERT INTO tc VALUES
('t1','c1','2021-09-03'), ('t1','c2','2021-09-04'),
('t2','c5','2021-03-06'), ('t2','c6','2021-09-06'),
('t3','c2','2021-03-08'), ('t3','c4','2021-09-08'),
('t4','c3','2021-03-10'),
('t5','c7','2021-09-12'), ('t5','c8','2021-03-15');
3-1

数学函数

讲义 · 数学函数
目的
对教师的工资做四舍五入和截断,再试几个取整和取余的函数。
前提
已执行数据准备。
  1. 查询教师姓名、工资、四舍五入到整数、截断到整数
  2. 计算 CEILING(-5.6)、FLOOR(-5.2)、MOD(10, 3)、ABS(-3.4)

参考答案

可以直接复制到 Workbench 执行
SELECT tn, sal, ROUND(sal), TRUNCATE(sal, 0) FROM t;
SELECT CEILING(-5.6), FLOOR(-5.2), MOD(10, 3), ABS(-3.4), ROUND(RAND() * 100);

语句解释

ROUND(x)四舍五入。ROUND(x, n) 保留 n 位小数
TRUNCATE(x, n)直接截去 n 位以后的小数,不四舍五入
CEILING 与 FLOOR向上取整和向下取整,负数时要注意方向
RAND()0 到 1 之间的随机数,每次执行都不同
执行后看:赵礼的工资 4267.90,ROUND 得 4268,TRUNCATE 得 4267。第二个查询前四项为 -5、-6、1、3.4。
3-2

字符串函数

讲义 · 字符串函数
目的
用字符串函数处理学生的姓名和院系:拼接、求长度、取姓、去掉“学院”两个字。
前提
已完成实验 3-1。
  1. 把姓名和院系拼成“王彤(信息学院)”的形式
  2. 求姓名的字符数,取出姓
  3. 把院系中的“学院”替换为空

参考答案

可以直接复制到 Workbench 执行
SELECT CONCAT(sn, '(', dept, ')') AS 姓名院系,
       CHAR_LENGTH(sn) AS 字数,
       LEFT(sn, 1) AS 姓,
       REPLACE(dept, '学院', '') AS 简称
FROM s;
SELECT LENGTH('北京'), CHAR_LENGTH('北京'), UPPER('beijing'), SUBSTRING('BEIJING', 1, 3);

语句解释

CONCAT把几个字符串连成一个
CHAR_LENGTH 与 LENGTH前者是字符数,后者是字节数,utf8mb4 中一个汉字占 3 个字节
LEFT(s, n)取左边 n 个字符
REPLACE(s, a, b)把 s 中的 a 全部替换为 b
执行后看:第一个查询 8 行,例如 王彤(信息学院)、2、王、信息。第二个查询得到 6、2、BEIJING、BEI。
3-3

日期和时间函数

讲义 · 日期和时间函数
目的
查看当前日期和时间,计算每门课开课至今的天数,并按中文格式显示开课日期。
前提
已完成实验 3-2。
  1. 查询 NOW()、CURDATE()、CURTIME()
  2. 查询授课表中的开课日期、年份、距今天数和中文格式的日期

参考答案

可以直接复制到 Workbench 执行
SELECT NOW(), CURDATE(), CURTIME();
SELECT tno, cno, tcdate,
       YEAR(tcdate) AS 年份,
       DATEDIFF(CURDATE(), tcdate) AS 距今天数,
       DATE_FORMAT(tcdate, '%Y年%m月%d日') AS 开课日期
FROM tc;
SELECT DATE_ADD('2021-02-06', INTERVAL 10 DAY), DAYNAME('2021-02-06');

语句解释

NOW()当前的日期和时间
DATEDIFF(d1, d2)d1 减 d2 相差的天数
DATE_FORMAT按格式符显示日期:%Y 四位年份,%m 两位月份,%d 两位日
DATE_ADD日期加上一段时间,INTERVAL 后写数量和单位
执行后看:距今天数随执行的日期而变。开课日期显示为 2021年09月03日 这样的形式。最后一句得到 2021-02-16 和 Saturday。
3-4

系统信息与加密函数

讲义 · 系统信息、加密与其他函数
目的
查看当前连接的版本、数据库、用户和连接号,再比较 MD5 和 SHA1 加密后的长度。
前提
已完成实验 3-3。
  1. 查询 VERSION()、DATABASE()、USER()、CONNECTION_ID()
  2. 对 123456 做 MD5 和 SHA1 加密,求加密结果的长度

参考答案

可以直接复制到 Workbench 执行
SELECT VERSION(), DATABASE(), USER(), CONNECTION_ID();
SELECT MD5('123456'), CHAR_LENGTH(MD5('123456')),
       SHA1('123456'), CHAR_LENGTH(SHA1('123456'));

语句解释

DATABASE()当前数据库,与 SCHEMA() 相同
USER()当前的用户名和主机
MD5 与 SHA1得到 32 位和 40 位的十六进制字符串,不能还原,常用来保存口令
上一章用户表的 cuspassword 就存放这样加密后的字符串
执行后看:数据库为 teaching,用户为 root@localhost。MD5 的结果以 e10adc39 开头,长度 32。SHA1 的长度 40。
3-5

聚合函数

讲义 · 聚合函数
目的
完成例 15-8:统计每位同学的选课门数和平均成绩,再比较 COUNT(*) 与 COUNT(score)。
前提
已完成实验 3-4。
  1. 按学号分组,求门数和保留两位小数的平均分
  2. 再求每人的成绩个数

参考答案

可以直接复制到 Workbench 执行
SELECT sno AS 学号, COUNT(*) AS 门数, ROUND(AVG(score), 2) AS 平均分
FROM sc GROUP BY sno;
SELECT sno, COUNT(*), COUNT(score) FROM sc GROUP BY sno;

语句解释

COUNT(*)统计行数,成绩为空的行也算
COUNT(score)只统计 score 不为空的行
AVG(score)不包括空值,s2 的 4 门课只按 3 个成绩求平均
ROUND(…, 2)平均分保留两位小数
执行后看:第一个查询 7 行,s6 没有选课,不出现。第二个查询中 s2、s4 两列不相等。
3-6

控制流与类型转换函数

讲义 · 系统信息、加密与其他函数
目的
用 IF 和 IFNULL 把 s2、s4 的成绩显示为及格情况,再用 CAST 和 CONVERT 转换类型。
前提
已完成实验 3-5。
  1. 成绩为空显示缺考
  2. 成绩不低于 60 显示及格,否则显示不及格
  3. 把字符串 3.14 转为数值再加 1

参考答案

可以直接复制到 Workbench 执行
SELECT sno, cno,
       IFNULL(score, '缺考') AS 成绩,
       IF(score >= 60, '及格', '不及格') AS 是否及格
FROM sc WHERE sno IN ('s2', 's4');
SELECT CAST('3.14' AS DECIMAL(5,2)) + 1, CONVERT(3.14, CHAR);

语句解释

IFNULL(v1, v2)v1 不为空返回 v1,否则返回 v2
IF(条件, v1, v2)条件为真返回 v1,否则返回 v2
成绩为空时score >= 60 的结果是 NULL,不算真,所以 IF 返回不及格
CAST 与 CONVERT把值转换为指定的类型
执行后看:s2 的 c7、s4 的 c6 显示缺考,是否及格一列却是不及格。第二个查询得到 4.14 和 3.14。
保山学院·人工智能教研室·曹鼎鼎