先用用户会话变量和系统变量,再看不改定界符时存储过程为什么建不起来,最后在语句块中使用局部变量。
MySQL 8.0 和 MySQL Workbench,机房电脑已装好。所有语句在 Workbench 的 SQL 编辑区中执行。
Ctrl+Enter 执行光标所在的一句,Ctrl+Shift+Enter 执行全部。Mac 上把 Ctrl 换成 ⌘。
输入法切到英文。表名、字段名不加引号,字符串值才用单引号。
重建 teaching 库的五张表和教材中的数据,并删除本章实验建立的存储过程和函数。
-- 第 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');
-- 这是单行注释 SET @a = 100, @b := 200; # 井号也是单行注释 /* 多行注释 可以写好几行 */ SELECT @a, @b, @a + @b; SELECT COUNT(*) INTO @n FROM s; SELECT @n AS 学生人数;
| -- 和 # | 单行注释。-- 后面要跟一个空格 |
| /* … */ | 多行注释,注释的内容不执行 |
| SET @a = 100 | 用户会话变量不用事先定义,也不用写数据类型,SET 中 = 和 := 都可以 |
| SELECT … INTO @n | 把查询结果存入变量 |
逐句执行,每执行一句,看一下结果。
SET @x = NULL; SELECT @x = 100; SELECT @x; SELECT @x := 100; SELECT @x;
| 语句 | 结果 | 原因 |
|---|---|---|
| 第 1 句 | 没有结果 | 先把 @x 清为 NULL,重做本实验时结果也一样 |
| 第 2 句 | NULL | 在 SELECT 中 = 是比较运算符。@x 的值为 NULL,比较的结果也是 NULL |
| 第 3 句 | NULL | 第 2 句没有给 @x 赋值 |
| 第 4 句 | 100 | := 才是赋值。MySQL 8.0 会给出警告 1287,建议改用 SET 或 SELECT … INTO |
| 第 5 句 | 100 | @x 已经赋值 |
SELECT @@version, @@autocommit, @@SESSION.sql_safe_updates; SHOW VARIABLES LIKE 'character_set%';
| @@变量名 | 系统变量前面加两个 @,由 MySQL 创建,不能自己定义 |
| @@SESSION. | 指定查会话变量,不写时先找会话变量,再找全局变量 |
| sql_safe_updates | 为 1 时,UPDATE 和 DELETE 必须用主码等作条件,Workbench 默认打开 |
| SHOW VARIABLES LIKE | 用通配符按名称查看一组系统变量 |
CREATE PROCEDURE s_count() BEGIN SELECT COUNT(*) FROM s; END;
| CREATE PROCEDURE | 建立存储过程,名称后面的括号不能省略,第 6 节细讲 |
| BEGIN … END | 语句块,把过程中的语句括在一起 |
| 第一个报错 1064 | 客户端遇到 FROM s 后面的分号就认为语句结束了,发给服务器的是不完整的 CREATE PROCEDURE |
| 第二个报错 1064 | 剩下的 END 单独成了一句 |
DELIMITER $$ CREATE PROCEDURE s_count() BEGIN SELECT COUNT(*) FROM s; END $$ DELIMITER ; CALL s_count();
| DELIMITER $$ | 告诉客户端,以后遇到 $$ 才算一条语句结束 |
| END $$ | 整个 CREATE PROCEDURE 作为一条语句发给服务器 |
| DELIMITER ; | 改回分号,注意 DELIMITER 和分号之间有空格 |
| CALL s_count() | 调用存储过程 |
DELIMITER $$ CREATE PROCEDURE info_count() BEGIN DECLARE n INT DEFAULT 0; SELECT COUNT(*) INTO n FROM s WHERE dept = '信息学院'; SELECT n AS 信息学院人数; END $$ DELIMITER ; CALL info_count(); -- 在语句块外面,n 不存在,报错 1054 SELECT n;
| DECLARE n INT DEFAULT 0 | 定义局部变量,要写数据类型,DEFAULT 给出初值 |
| 位置 | DECLARE 只能写在 BEGIN … END 中,并且在其他语句之前 |
| 报错 1054 | Unknown column,局部变量只在定义它的语句块中有效 |