课程 › 第 15 章 › 实验一
第 15 章 · 实验一 · 在第二节之后

定界符、语句块与变量

先用用户会话变量和系统变量,再看不改定界符时存储过程为什么建不起来,最后在语句块中使用局部变量。

环 境

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

执 行

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

注 意

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

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

  1. 实验 1-1注释与用户会话变量
  2. 实验 1-2SELECT 中的 = 与 :=
  3. 实验 1-3系统变量
  4. 实验 1-4不改定界符
  5. 实验 1-5用 DELIMITER 改定界符
  6. 实验 1-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');
1-1

注释与用户会话变量

讲义 · 注释讲义 · 变量与用户会话变量
目的
用三种注释给语句加说明,用 SET 和 SELECT … INTO 给用户会话变量赋值,再查看变量的值。
前提
已执行数据准备。
  1. 用 SET 给 @a、@b 赋值,查询 @a + @b
  2. 把学生人数存入 @n,再查看 @n
  3. 语句中分别用上三种注释

参考答案

可以直接复制到 Workbench 执行
-- 这是单行注释
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把查询结果存入变量
执行后看:第一个查询得到 100、200、300。学生人数为 8。
1-2

SELECT 中的 = 与 :=

讲义 · 变量与用户会话变量
目的
执行下面五句,看 = 和 := 在 SELECT 中有什么不同。
前提
已完成实验 1-1。

逐句执行,每执行一句,看一下结果。

要执行的语句
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 中给变量赋值要用 :=,例如 SELECT @a := 100, @b := 200。
1-3

系统变量

讲义 · 系统变量
目的
查看几个系统变量的值:MySQL 的版本、是否自动提交、Workbench 的安全更新模式。
前提
已完成实验 1-2。
  1. 用 @@ 查询 version、autocommit、sql_safe_updates
  2. 用 SHOW VARIABLES 查看名称以 character_set 开头的变量

参考答案

可以直接复制到 Workbench 执行
SELECT @@version, @@autocommit, @@SESSION.sql_safe_updates;
SHOW VARIABLES LIKE 'character_set%';

语句解释

@@变量名系统变量前面加两个 @,由 MySQL 创建,不能自己定义
@@SESSION.指定查会话变量,不写时先找会话变量,再找全局变量
sql_safe_updates为 1 时,UPDATE 和 DELETE 必须用主码等作条件,Workbench 默认打开
SHOW VARIABLES LIKE用通配符按名称查看一组系统变量
执行后看:版本以 8.0 开头,autocommit 为 1,在 Workbench 中 sql_safe_updates 为 1。第二个查询列出八个左右的字符集变量。
1-4

不改定界符

讲义 · 定界符与语句块
目的
不改定界符,直接建一个查询学生人数的存储过程,看会发生什么。
前提
已完成实验 1-3。
  1. 不用 DELIMITER,直接执行下面的 CREATE PROCEDURE

参考答案

可以直接复制到 Workbench 执行
CREATE PROCEDURE s_count()
BEGIN
  SELECT COUNT(*) FROM s;
END;

语句解释

CREATE PROCEDURE建立存储过程,名称后面的括号不能省略,第 6 节细讲
BEGIN … END语句块,把过程中的语句括在一起
第一个报错 1064客户端遇到 FROM s 后面的分号就认为语句结束了,发给服务器的是不完整的 CREATE PROCEDURE
第二个报错 1064剩下的 END 单独成了一句
执行后看:两个报错都是 1064。存储过程没有建立。
1-5

用 DELIMITER 改定界符

讲义 · 定界符与语句块
目的
先把定界符改为 $$,再建立同一个存储过程,改回分号后调用它。
前提
已完成实验 1-4。
  1. 用 DELIMITER $$ 改定界符
  2. 建立 s_count,以 $$ 结束
  3. 把定界符改回分号,调用 s_count

参考答案

可以直接复制到 Workbench 执行
DELIMITER $$
CREATE PROCEDURE s_count()
BEGIN
  SELECT COUNT(*) FROM s;
END $$
DELIMITER ;
CALL s_count();

语句解释

DELIMITER $$告诉客户端,以后遇到 $$ 才算一条语句结束
END $$整个 CREATE PROCEDURE 作为一条语句发给服务器
DELIMITER ;改回分号,注意 DELIMITER 和分号之间有空格
CALL s_count()调用存储过程
执行后看:调用得到 8。在左侧 teaching 库的 Stored Procedures 下刷新,可以看到 s_count。
1-6

局部变量

讲义 · 局部变量
目的
在存储过程的语句块中用 DECLARE 定义局部变量,存入信息学院的人数,再在块外查询这个变量。
前提
已完成实验 1-5。
  1. 建立 info_count,用局部变量 n 存放信息学院的人数
  2. 调用 info_count
  3. 在过程外面查询 n

参考答案

可以直接复制到 Workbench 执行
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 中,并且在其他语句之前
报错 1054Unknown column,局部变量只在定义它的语句块中有效
执行后看:调用得到 4。最后一句报错 1054。
保山学院·人工智能教研室·曹鼎鼎