第一章 绪论
1.1 数据库系统概论
1.1.3 数据库系统的特点
使用数据库系统进行学生学籍管理
1.建表:
-- 建立STUDENT表
create table STUDENT
(
Sno char(8), --字符串数据类型,固定长度
Sname char(10),
Ssex char(2),
Sage smallint, --精确数值数据类型
Major char(20)
);
-- create table char等可大写、可小写
-- 建立AWARD表
create table AWARD
(
Sno char(8),
Details varchar(2000) --字符串数据类型,可变长度
);
2.插入信息
/*插入学生的
基本信息*/ --多行注释
insert into STUDENT
(
sno,
sname,
ssex,
sage,
major
)
values
(
'20101',
'史玉明',
'女',
20,
'计算机'
)
--插入学生获得的奖励
insert into AWARD
(
sno,
details
)
values
(
'20101',
'2011校奖学金,2012国家奖学金'
)
insert into STUDENT(sno,sname,ssex,sage,major)
values('19210','李明虎','男',21,'机械')
insert into STUDENT(sno,sname,ssex,sage,major)
values('19334','张翔','男',21,'化工')
insert into AWARD(sno,details)
values('19210','2012校优秀学生')
3.查询学生信息
select * from STUDENT --查看STUDENT表里所有数据
select * from AWARD --查看AWARD表里所有数据
--查看包含奖励信息的所有学生信息
select
A.*,
B.Details
from
STUDENT A
left join
AWARD B
on A.Sno=B.Sno
第三章 关系数据库标准语言SQL
3.3 数据定义
3.3.1 模式的定义与删除
-
定义模式
在SQL中,模式定义语句如下:
CREATE SCHEMA<模式名> AUTHORIZATION <用户名>;
在CREATE SCHEMA中可以接受CREATE TABLE,CREATE VIEW和GRANT子句
CREATE SCHEMA <模式名> AUTHORIZATION <用户名>[<表定义子句>|<视图定义子句>|<授权定义子句>]
【例3.1】为用户WANG定义一个学生-课程模式S-T
CREATE SCHEMA “S-T” AUTHORIZATION WANG;
【例3.2】为用户WANG定义一个模式,不指定<模式名>,<模式名>隐含为<用户名>
CREATE SCHEMA AUTHORIZATION WANG;
【例3.3】为用户ZHANG创建了一个模式TEST,并且在其中定义一个表TAB1
CREATE SCHEMA TEST AUTHORIZATION ZHANG
CREATE TABLE TAB1 ( COL1 SMALLINT,
COL2 INT,
COL3 CHAR(20),
COL4 NUMERIC(10,3),
COL5 DECIMAL(5,2)
);
-
删除模式
在SQL中,模式删除语句如下:
DROP SCHEMA <模式名> <CASCADE|RESTRICT>;
CASCADE(级联):删除模式的同时把该模式中所有的数据库对象全部删除
RESTRICT(限制):如果该模式中定义了下属的数据库对象(如表、视图等),则拒绝该删除语句的执行。仅当该模式中没有任何下属的对象时才能执行。
【例3.4】删除模式ZHANG,同时该模式中定义的表TAB1也被删除
DROP SCHEMA ZHANG CASCADE;
-
MySQL中定义、删除数据库
定义数据库
CREATE DATABASE 数据库名;
删除数据库
DROP DATABASE 数据库名;
3.3.2 基本表的定义与删除
-
定义基本表
定义基本表语句如下:
CREATE TABLE <表名>
(<列名> <数据类型>[ <列级完整性约束条件> ]
[,<列名> <数据类型>[ <列级完整性约束条件>] ]
…
[,<表级完整性约束条件> ] );
<表名>:所要定义的基本表的名字
<列名>:组成该表的各个属性(列)
<列级完整性约束条件>:涉及相应属性列的完整性约束条件
<表级完整性约束条件>:涉及一个或多个属性列的完整性约束条件
如果完整性约束条件涉及到该表的多个属性列,则必须定义在表级上,否则既可以定义在列级也可以定义在表级。
【例3.5】建立“学生”表Student。学号是主码,姓名取值唯一。
CREATE TABLE Student
(Sno CHAR(9) PRIMARY KEY, /* 列级完整性约束条件,Sno是主码*/
Sname CHAR(20) UNIQUE, /* Sname取唯一值*/
Ssex CHAR(2),
Sage SMALLINT,
Sdept CHAR(20)
);
desc Student;-- 创建后查看属性
【例3.6】建立一个“课程”表Course。
CREATE TABLE Course
(Cno CHAR(4) PRIMARY KEY, /*列级完整性约束条件,Cno为主码*/
Cname CHAR(40) NOT NULL, /*列级完整性约束条件,Cname不能取空值*/
Cpno CHAR(4), /*Cpno的含义为先修课*/
Ccredit SMALLINT,
FOREIGN KEY (Cpno) REFERENCES Course(Cno)
/*表级完整性约束条件,Cpno是外码,被参照表是Course,被参照列是Cno*/
);
desc Course;-- 创建后查看属性
【例3.7】建立一个学生选课表SC。
CREATE TABLE SC
(Sno CHAR(9),
Cno CHAR(4),
Grade SMALLINT,
PRIMARY KEY (Sno,Cno),
/* 主码由两个属性构成,必须作为表级完整性进行定义*/
FOREIGN KEY (Sno) REFERENCES Student(Sno),
/* 表级完整性约束条件,Sno是外码,被参照表是Student */
FOREIGN KEY (Cno)REFERENCES Course(Cno)
/* 表级完整性约束条件, Cno是外码,被参照表是Course*/
);
desc SC;-- 创建后查看属性
查看已添加数据表
show tables;
4.修改基本表
修改基本表语句如下:
ALTER TABLE <表名>
[ADD[COLUMN] <新列名> <数据类型> [完整性约束]]
[ADD <表级完整性约束>]
[DROP [COLUMN] <列名> [CASCADE| RESTRICT]]
[DROP CONSTRAINT<完整性约束名>[ RESTRICT | CASCADE ]]
[ALTER COLUMN <列名><数据类型>];
<表名>是要修改的基本表
ADD子句用于增加新列、新的列级完整性约束条件和新的表级完整性约束条件
DROP COLUMN子句用于删除表中的列
如果指定了CASCADE短语,则自动删除引用了该列的其他对象
如果指定了RESTRICT短语,则如果该列被其他对象引用,关系数据库管理系统将拒绝删除该列
DROP CONSTRAINT子句用于删除指定的完整性约束条件
ALTER COLUMN子句用于修改原有的列定义,包括修改列名和数据类型
【例3.8】向Student表增加“入学时间”列,其数据类型为日期型。
ALTER TABLE Student ADD S_entrance DATE;
不管基本表中原来是否已有数据,新增加的列一律为空值
【例3.9】将年龄的数据类型由字符型(假设原来的数据类型是字符型)改为整数。
ALTER TABLE Student ALTER COLUMN Sage INT;
【例3.10】增加课程名称必须取唯一值的约束条件。
ALTER TABLE Course ADD UNIQUE(Cname);
5.删除基本表
删除基本表语句如下:
DROP TABLE <表名> [RESTRICT| CASCADE];
RESTRICT:删除表是有限制的。欲删除的基本表不能被其他表的约束所引用。如果存在依赖该表的对象,则此表不能被删除。
CASCADE:删除该表没有限制。在删除基本表的同时,相关的依赖对象一起删除。
默认为RESTRICT
【例3.11】删除Student表。
DROP TABLE Student CASCADE;
基本表定义被删除,数据被删除;表上建立的索引、视图、触发器等一般也将被删除。
【例3.12】若表上建有视图,选择RESTRICT时表不能删除;选择CASCADE时可以删除表,视图也自动删除。
CREATE VIEW IS_Student /*Student表上建立视图*/
AS
SELECT Sno,Sname,Sage
FROM Student
WHERE Sdept='IS';
DROP TABLE Student RESTRICT; /*删除Student表*/
--ERROR: cannot drop table Student because other objects depend on it
/*系统返回错误信息,存在依赖该表的对象,此表不能被删除*/
DROP TABLE Student CASCADE; /*删除Student表*/
--NOTICE: drop cascades to view IS_Student
/*系统返回提示信息,此表上的视图也被删除*/
SELECT * FROM IS_Student;
--ERROR: relation " IS_Student " does not exist
3.3.3 索引的建立与删除
-
建立索引
建立索引语句如下:
CREATE [UNIQUE] [CLUSTER] INDEX <索引名>
ON <表名>(<列名>[<次序>][,<列名>[<次序>] ]…);
<表名>:要建索引的基本表的名字
索引:可以建立在该表的一列或多列上,各列名之间用逗号分隔
<次序>:指定索引值的排列次序,升序:ASC,降序:DESC。缺省值:ASC
UNIQUE:此索引的每一个索引值只对应唯一的数据记录
CLUSTER:表示要建立的索引是聚簇索引
【例3.13】 为学生-课程数据库中的Student,Course,SC三个表建立索引。Student表按学号升序建唯一索引,Course表按课程号升序建唯一索引,SC表按学号升序和课程号降序建唯一索引
CREATE UNIQUE INDEX Stusno ON Student(Sno);
CREATE UNIQUE INDEX Coucno ON Course(Cno);
CREATE UNIQUE INDEX SCno ON SC(Sno ASC,Cno DESC);
-
修改索引
修改索引语句如下:
ALTER INDEX <旧索引名> RENAME TO <新索引名>
【例3.14】 将SC表的SCno索引名改为SCSno
ALTER INDEX SCno RENAME TO SCSno;
-
删除索引
删除索引语句如下:
DROP INDEX <索引名>;
【例3.15】 删除Student表的Stusname索引
DROP INDEX Stusname;
3.4 数据查询
数据查询语句如下:
SELECT [ALL|DISTINCT] <目标列表达式>[,<目标列表达式>] …
FROM <表名或视图名>[,<表名或视图名> ]…|(SELECT 语句) [AS]<别名>
[WHERE <条件表达式>]
[GROUP BY <列名1> [ HAVING <条件表达式> ]]
[ORDER BY <列名2> [ ASC|DESC ]];
SELECT子句:指定要显示的属性列
FROM子句:指定查询对象(基本表或视图)
WHERE子句:指定查询条件
GROUP BY子句:对查询结果按指定列的值分组,该属性列值相等的元组为一个组。通常会在每组中作用聚集函数。
HAVING短语:只有满足指定条件的组才予以输出
ORDER BY子句:对查询结果表按指定列值的升序或降序排序
3.4.1 单表查询
-
选择表中的若干列
(1)查询指定列
【例3.16】 查询全体学生的学号与姓名。
SELECT Sno,Sname
FROM Student;
【例3.17】 查询全体学生的姓名、学号、所在系。
SELECT Sname,Sno,Sdept
FROM Student;
(2)查询全部列
【例3.18】 查询全体学生的详细记录。
SELECT Sno,Sname,Ssex,Sage,Sdept
FROM Student;
等价于
SELECT * FROM Student;
(3)查询经过计算的值
【例3.19】 查询全体学生的姓名及其出生年份。
SELECT Sname,2023-Sage /*假设当时为2023年*/
FROM Student;
SELECT Sname,YEAR(NOW())-Sage /*取当下年份*/
FROM Student;
【例3.20】 查询全体学生的姓名、出生年份和所在的院系,要求用小写字母表示系名。
SELECT Sname,'Year of Birth:',2014-Sage,LOWER(Sdept)
FROM Student;
使用列别名改变查询结果的列标题
SELECT
Sname NAME,
'Year of Birth:' AS BIRTH, /*AS可写可不写*/
2023 - Sage BIRTHDAY,
LOWER(Sdept) DEPARTMENT
FROM
Student;
-
选择表中的若干元组
(1)消除取值重复的行
通过DISTINCT实现。如果没有指定DISTINCT关键词,则缺省为ALL
【例3.21】 查询选修了课程的学生学号。
SELECT Sno FROM SC;
等价于
SELECT ALL Sno FROM SC;
SELECT DISTINCT Sno FROM SC;
(2)查询满足条件的元组
通过WHERE子句实现。
-
比较:
【例3.22】 查询计算机科学系全体学生的名单。
SELECT Sname
FROM Student
WHERE Sdept = 'CS';
【例3.23】 查询所有年龄在20岁以下的学生姓名及其年龄。
SELECT Sname,Sage
FROM Student
WHERE Sage < 20;
【例3.24】 查询考试成绩有不及格的学生的学号。
SELECT DISTINCT Sno
FROM SC
WHERE Grade < 60;
-
确定范围:
【例3.25】 查询年龄在20~23岁(包括20岁和23岁)之间的学生的姓名、系别和年龄
SELECT Sname, Sdept, Sage
FROM Student
WHERE Sage BETWEEN 20 AND 23;
【例3.26】 查询年龄不在20~23岁之间的学生姓名、系别和年龄
SELECT Sname, Sdept, Sage
FROM Student
WHERE Sage NOT BETWEEN 20 AND 23;
-
确定集合:
【例3.27】 查询计算机科学系(CS)、数学系(MA)和信息系(IS)学生的姓名和性别。
SELECT Sname, Ssex
FROM Student
WHERE Sdept IN ('CS','MA’,'IS' );
【例3.28】 查询既不是计算机科学系、数学系,也不是信息系的学生的姓名和性别。
SELECT Sname, Ssex
FROM Student
WHERE Sdept NOT IN ('IS','MA’,'CS' );
-
字符匹配:
[NOT] LIKE ‘<匹配串>’ [ESCAPE ‘ <换码字符>’]
<匹配串>可以是一个完整的字符串,也可以含有通配符%和 _
% (百分号) : 代表任意长度(长度可以为0)的字符串
例如a%b表示以a开头,以b结尾的任意长度的字符串
_ (下横线) :代表任意单个字符。
例如a_b表示以a开头,以b结尾的长度为3的任意字符串
匹配串为固定字符串:
【例3.29】 查询学号为201215121的学生的详细情况。
SELECT *
FROM Student
WHERE Sno LIKE '201215121';
等价于
SELECT *
FROM Student
WHERE Sno = '201215121';
匹配串为含通配符的字符串:
【例3.30】 查询所有姓刘学生的姓名、学号和性别。
SELECT Sname, Sno, Ssex
FROM Student
WHERE Sname LIKE '刘%';
【例3.31】 查询姓"欧阳"且全名为三个汉字的学生的姓名。
SELECT Sname
FROM Student
WHERE Sname LIKE '欧阳_';
【例3.32】 查询名字中第2个字为"阳"字的学生的姓名和学号。
SELECT Sname, Sno
FROM Student
WHERE Sname LIKE '_阳%';
【例3.33】 查询所有不姓刘的学生姓名、学号和性别。
SELECT Sname, Sno, Ssex
FROM Student
WHERE Sname NOT LIKE '刘%';
使用换码字符将通配符转义为普通字符:
【例3.34】 查询DB_Design课程的课程号和学分。
SELECT Cno,Ccredit
FROM Course
WHERE Cname LIKE 'DB\_Design' ESCAPE '\ ' ;
【例3.35】 查询以"DB_"开头,且倒数第3个字符为 i的课程的详细情况。
SELECT *
FROM Course
WHERE Cname LIKE 'DB\_%i_ _' ESCAPE '\ ' ;
-
涉及空值的查询:
【例3.36】 某些学生选修课程后没有参加考试,所以有选课记录,但没有考试成绩。查询缺少成绩的学生的学号和相应的课程号。
SELECT Sno,Cno
FROM SC
WHERE Grade IS NULL;
【例3.37】 查询所有有成绩的学生学号和课程号。
SELECT Sno,Cno
FROM SC
WHERE Grade IS NOT NULL;
-
多重条件查询:
【例3.38】 查询计算机系年龄在20岁以下的学生姓名。
SELECT Sname
FROM Student
WHERE Sdept = 'CS' AND Sage < 20;
【例3.27改写】 查询计算机科学系(CS)、数学系(MA)和信息系(IS)学生的姓名和性别。
SELECT Sname, Ssex
FROM Student
WHERE Sdept= 'CS' OR Sdept= 'MA' OR Sdept= 'IS';
-
ORDER BY子句
【例3.39】 查询选修了3号课程的学生的学号及其成绩,查询结果按分数降序排列。
SELECT Sno, Grade
FROM SC
WHERE Cno= '3'
ORDER BY Grade DESC;
【例3.40】 查询全体学生情况,查询结果按所在系的系号升序排列,同一系中的学生按年龄降序排列。
SELECT *
FROM Student
ORDER BY Sdept, Sage DESC;
-
聚集函数
【例3.41】 查询学生总人数。
SELECT COUNT(*) FROM Student;
【例3.42】 查询选修了课程的学生人数。
SELECT COUNT(DISTINCT Sno) FROM SC;
【例3.43】 计算1号课程的学生平均成绩。
SELECT AVG(Grade)
FROM SC
WHERE Cno = '1';
【例3.44】 查询选修1号课程的学生最高分数。
SELECT MAX(Grade)
FROM SC
WHERE Cno='1';
【例3.45】 查询学生201215012选修课程的总学分数。
SELECT SUM(Ccredit)
FROM SC,Course
WHERE Sno='201215012' AND SC.Cno=Course.Cno;
-
GROUP BY 子句
【例3.46】 求各个课程号及相应的选课人数。
SELECT Cno,COUNT(Sno)
FROM SC
GROUP BY Cno;
【例3.47】 查询选修了3门以上课程的学生学号。
SELECT Sno
FROM SC
GROUP BY Sno
HAVING COUNT(*) > 3;
【例3.48】 查询平均成绩大于等于90分的学生学号和平均成绩。
SELECT Sno, AVG(Grade)
FROM SC
WHERE AVG(Grade) >= 90
GROUP BY Sno;
因为WHERE子句中是不能用聚集函数作为条件表达式,正确的查询语句应该是:
SELECT Sno, AVG(Grade)
FROM SC
GROUP BY Sno
HAVING AVG(Grade) >= 90;
3.4.2 连接查询
-
等值与非等值连接查询
【例3.49】 查询每个学生及其选修课程的情况。
SELECT Student.*, SC.*
FROM Student, SC
WHERE Student.Sno = SC.Sno;
【例3.50】 查询每个学生及其选修课程的情况,使用自然连接。
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student,SC
WHERE Student.Sno = SC.Sno;
【例3.51】 查询选修2号课程且成绩在90分以上的所有学生的学号和姓名。
SELECT Student.Sno, Sname
FROM Student, SC
WHERE Student.Sno = SC.Sno
AND SC.Cno = '2'
AND SC.Grade > 90;
-
自身连接查询
【例3.52】 查询每一门课的间接先修课(即先修课的先修课)。
SELECT FIRST.Cno, SECOND.Cpno
FROM Course FIRST, Course SECOND
WHERE FIRST.Cpno = SECOND.Cno;
-
外连接查询
【例3.53】 查询每个学生及其选修课程的情况,改写。
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student LEFT OUTER JOIN SC
ON (Student.Sno=SC.Sno);
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student LEFT OUTER JOIN SC
ON Student.Sno=SC.Sno
AND Student.Sno = 201215121;
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student LEFT OUTER JOIN SC
ON Student.Sno=SC.Sno
WHERE Student.Sno = 201215121;
-
多表连接查询
【例3.54】 查询每个学生的学号、姓名、选修的课程名及成绩。
SELECT Student.Sno, Sname, Cname, Grade
FROM Student, SC, Course /*多表连接*/
WHERE Student.Sno = SC.Sno
AND SC.Cno = Course.Cno;
3.4.3 嵌套查询
SELECT Sname /*外层查询/父查询*/
FROM Student
WHERE
Sno IN
( SELECT Sno /*内层查询/子查询*/
FROM SC
WHERE Cno= '2');
-
带有IN谓词的子查询
【例3.55】 查询与“刘晨”在同一个系学习的学生。
/*确定“刘晨”所在系名*/
SELECT Sdept
FROM Student
WHERE Sname= '刘晨';
/*结果为CS*/
/*查找所有在CS系学习的学生。*/
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept= 'CS';
/*将第一步查询嵌入到第二步查询的条件中*/
SELECT Sno, Sname, Sdept
FROM Student
WHERE
Sdept IN (SELECT Sdept
FROM Student
WHERE Sname= '刘晨'
);
SELECT S1.Sno, S1.Sname,S1.Sdept
FROM Student S1,Student S2
WHERE
S1.Sdept = S2.Sdept AND
S2.Sname = '刘晨';
【例3.56】 查询选修了课程名为“信息系统”的学生学号和姓名。
SELECT Sno,Sname /*最后在Student关系中取出Sno和Sname*/
FROM Student
WHERE
Sno IN (SELECT Sno /*然后在SC关系中找出选修了3号课程的学生学号*/
FROM SC
WHERE
Cno IN (SELECT Cno /*首先在Course表中找出“信息系统”的课程号,为3号*/
FROM Course
WHERE Cname= '信息系统'
)
);
SELECT Sno,Sname
FROM Student,SC,Course
WHERE
Student.Sno = SC.Sno AND
SC.Cno = Course.Cno AND
Course.Cname='信息系统';
-
带有比较运算符的子查询
【例3.57】 找出每个学生不小于他选修课程平均成绩的课程号。
SELECT Sno, Cno
FROM SC x
WHERE
Grade >= (SELECT AVG(Grade)
FROM SC y
WHERE y.Sno=x.Sno
);
-
带有ANY(SOME)或ALL谓词的子查询
【例3.58】 查询非计算机科学系中比计算机科学系任意一个学生年龄小的学生姓名和年龄。
SELECT Sname,Sage
FROM Student
WHERE
Sage < ANY (SELECT Sage
FROM Student
WHERE Sdept= 'CS'
)
AND Sdept <> 'CS' ; /*父查询块中的条件 */
SELECT Sname,Sage
FROM Student
WHERE
Sage < (SELECT MAX(Sage)
FROM Student
WHERE Sdept = 'CS'
)
AND Sdept <> 'CS';
【例3.59】 查询非计算机科学系中比计算机科学系所有学生年龄都小的学生姓名及年龄。
SELECT Sname,Sage
FROM Student
WHERE
Sage < ALL (SELECT Sage
FROM Student
WHERE Sdept= 'CS'
)
AND Sdept <> 'CS' ;
SELECT Sname,Sage
FROM Student
WHERE
Sage < (SELECT MIN(Sage)
FROM Student
WHERE Sdept = 'CS'
)
AND Sdept <> 'CS';
-
带有EXISTS谓词的子查询
【例3.60】 查询所有选修了1号课程的学生姓名。
SELECT Sname
FROM Student
WHERE
EXISTS (SELECT *
FROM SC
WHERE
Sno = Student.Sno
AND Cno = '1'
);
【例3.61】 查询没有选修1号课程的学生姓名。
SELECT Sname
FROM Student
WHERE
NOT EXISTS (SELECT *
FROM SC
WHERE
Sno = Student.Sno
AND Cno = '1'
);
【例3.55】 查询与“刘晨”在同一个系学习的学生。可以用带EXISTS谓词的子查询替换。
SELECT Sno, Sname, Sdept
FROM Student S1
WHERE
EXISTS (SELECT *
FROM Student S2
WHERE
S2.Sdept = S1.Sdept
AND S2.Sname = '刘晨'
);
【例3.62】查询选修了全部课程的学生姓名。
SELECT Sname
FROM Student
WHERE
NOT EXISTS (SELECT *
FROM Course
WHERE
NOT EXISTS (SELECT *
FROM SC
WHERE
Sno = Student.Sno
AND Cno= Course.Cno
)
);
【例3.63】查询至少选修了学生201215122选修的全部课程的学生号码。
SELECT DISTINCT Sno
FROM SC SCX
WHERE
NOT EXISTS (SELECT *
FROM SC SCY
WHERE
SCY.Sno = '201215122'
AND NOT EXISTS (SELECT *
FROM SC SCZ
WHERE
SCZ.Sno=SCX.Sno
AND SCZ.Cno=SCY.Cno
)
);
3.4.4 集合查询
【例3.64】查询计算机科学系的学生及年龄不大于19岁的学生。
SELECT *
FROM Student
WHERE
Sdept= 'CS'
OR Sage <= 19;
SELECT *
FROM Student
WHERE Sdept= 'CS'
UNION
SELECT *
FROM Student
WHERE Sage <= 19;
【例3.65】查询选修了课程1或者选修了课程2的学生。
SELECT Sno
FROM SC
WHERE
Cno = '1'
OR Cno = '2';
SELECT Sno
FROM SC
WHERE Cno = '1'
UNION
SELECT Sno
FROM SC
WHERE Cno = '2';
【例3.66】查询计算机科学系的学生与年龄不大于19岁的学生的交集。
SELECT *
FROM Student
WHERE Sdept = 'CS'
INTERSECT
SELECT *
FROM Student
WHERE Sage <= 19;
SELECT *
FROM Student
WHERE
Sdept = 'CS'
AND Sage <= 19;
【例3.67】查询既选修了课程1又选修了课程2的学生
SELECT Sno
FROM SC
WHERE Cno = '1'
INTERSECT
SELECT Sno
FROM SC
WHERE Cno = '2';
SELECT Sno
FROM SC
WHERE
Cno = '1'
AND Sno IN (SELECT Sno
FROM SC
WHERE Cno = '2'
);
【例3.68】查询计算机科学系的学生与年龄不大于19岁的学生的差集
SELECT *
FROM Student
WHERE Sdept = 'CS'
EXCEPT
SELECT *
FROM Student
WHERE Sage <= 19;
SELECT *
FROM Student
WHERE
Sdept = 'CS'
AND Sage > 19;
3.4.5 基于派生表的查询
【例3.57】 找出每个学生不小于他选修课程平均成绩的课程号。
SELECT Sno, Cno
FROM SC,
(SELECT Sno, Avg(Grade)
FROM SC
GROUP BY Sno
) AS Avg_sc(avg_sno,avg_grade)
WHERE
SC.Sno = Avg_sc.avg_sno
AND SC.Grade >= Avg_sc.avg_grade
【例3.60】 查询所有选修了1号课程的学生姓名。
SELECT Sname
FROM Student,
(SELECT Sno
FROM SC
WHERE Cno = '1'
) AS SC1
WHERE Student.Sno = SC1.Sno;
3.4.6 SELECT语句的一般格式
SELECT [ALL|DISTINCT] <目标列表达式> [别名] [,<目标列表达式> [别名]] …
FROM <表名或视图名> [别名] [,<表名或视图名> [别名]] …|(<SELECT语句>)[AS]<别名>
[WHERE <条件表达式>]
[GROUP BY <列名1>[HAVING<条件表达式>]]
[ORDER BY <列名2> [ASC|DESC]];
3.5 数据更新
3.5.1 插入数据
-
插入元组
【例3.69】将一个新学生元组(学号:201215128;姓名:陈冬;性别:男;所在系:IS;年龄:18岁)插入到Student表中。
INSERT
INTO Student (Sno,Sname,Ssex,Sdept,Sage)
VALUES ('201215128','陈冬','男','IS',18);
【例3.70】将学生张成民的信息插入到Student表中。
INSERT
INTO Student
VALUES ('201215126','张成民','男’,18,'CS');
【例3.71】插入一条选课记录( '200215128','1 ')。
INSERT
INTO SC(Sno,Cno)
VALUES ('201215128','1');
INSERT
INTO SC
VALUES ('201215128','1',NULL);
-
插入子查询结果
【例3.72】对每一个系,求学生的平均年龄,并把结果存入数据库。
/*建表*/
CREATE TABLE Dept_age
(Sdept CHAR(15) /*系名*/
Avg_age SMALLINT /*学生平均年龄*/
);
/*插入数据*/
INSERT
INTO Dept_age (Sdept,Avg_age)
SELECT Sdept,AVG(Sage)
FROM Student
GROUP BY Sdept;
批量插入元组
【补例3.72】插入选课记录( '200215128','1‘), ( '200215128',’2‘), ( '200215129','1‘) 。
INSERT
INTO SC
VALUES
('201215128','1',NULL),
('201215128','2',NULL),
('201215129','1',NULL);
3.5.2 修改数据
-
修改某一个元组的值
【例3.73】将学生201215121的年龄改为22岁。
UPDATE Student
SET Sage = 22
WHERE Sno = '201215121';
-
修改多个元组的值
【例3.74】将所有学生的年龄增加1岁。
UPDATE Student
SET Sage = Sage + 1;
-
带子查询的修改语句
【例3.75】将计算机科学系全体学生的成绩置零。
UPDATE SC
SET Grade=0
WHERE
Sno IN (SELECT Sno
FROM Student
WHERE Sdept = 'CS'
);
3.5.3 删除数据
-
删除某一个元组的值
【例3.76】删除学号为201215128的学生记录。
DELETE
FROM Student
WHERE Sno = '201215128';
-
删除多个元组的值
【例3.77】删除所有的学生选课记录。
DELETE
FROM SC;
-
带子查询的删除语句
【例3.78】删除计算机科学系所有学生的选课记录。
DELETE
FROM SC
WHERE
Sno IN (SELECT Sno
FROM Student
WHERE Sdept = 'CS'
);
3.6 空值的处理
3.6.1 空值的产生
【例3.79】向SC表中插入一个元组,学生号是"201215126",课程号是"1",成绩为空。
INSERT INTO SC(Sno,Cno,Grade)
VALUES('201215126','1',NULL); /*该学生还没有考试成绩,取空值*/
INSERT INTO SC(Sno,Cno)
VALUES('201215126','1'); /*没有赋值的属性,其值为空值*/
【例3.80】将Student表中学生号为"201215200"的学生所属的系改为空值。
UPDATE Student
SET Sdept = NULL
WHERE Sno = '201215200';
3.6.2 空值的判断
【例3.81】从Student表中找出漏填了数据的学生信息。
SELECT *
FROM Student
WHERE
Sname IS NULL
OR Ssex IS NULL
OR Sage IS NULL
OR Sdept IS NULL;
3.6.4 空值的算术运算、比较运算和逻辑运算
【例3.82】找出选修1号课程的不及格的学生。
SELECT Sno
FROM SC
WHERE
Grade < 60
AND Cno = '1';
/*查询结果不包括缺考的学生,因为他们的Grade值为NULL*/
【例3.83】选出选修1号课程的不及格的学生以及缺考的学生。
SELECT Sno
FROM SC
WHERE
Grade < 60
AND Cno = '1'
UNION
SELECT Sno
FROM SC
WHERE
Grade IS NULL
AND Cno = '1';
SELECT Sno
FROM SC
WHERE
Cno = '1'
AND (Grade < 60
OR Grade IS NULL);
3.7 视图
3.7.1 定义视图
1.建立视图
【例3.84】建立信息系学生的视图。
CREATE VIEW IS_Student
AS
SELECT Sno,Sname,Sage
FROM Student
WHERE Sdept = 'IS';
【例3.85】建立信息系学生的视图,并要求进行修改和插入操作时仍需保证该视图只有信息系的学生。
CREATE VIEW IS_Student
AS
SELECT Sno,Sname,Sage
FROM Student
WHERE Sdept = 'IS';
WITH CHECK OPTION;
【例3.86】建立信息系选修了1号课程的学生的视图(包括学号、姓名、成绩)。
CREATE VIEW IS_S1(Sno,Sname,Grade)
AS
SELECT Student.Sno,Sname,Grade
FROM Student,SC
WHERE
Sdept = 'IS'
AND Student.Sno = SC.Sno
AND SC.Cno = '1';
【例3.87】建立信息系选修了1号课程且成绩在90分以上的学生的视图。
CREATE VIEW IS_S2
AS
SELECT Sno,Sname,Grade
FROM IS_S1
WHERE Grade >= 90;
【例3.88】定义一个反映学生出生年份的视图。
CREATE VIEW BT_S(Sno,Sname,Sbirth)
AS
SELECT Sno,Sname,2014-Sage
FROM Student;
【例3.89】将学生的学号及平均成绩定义为一个视图。
CREAT VIEW S_G(Sno,Gavg)
AS
SELECT Sno,AVG(Grade)
FROM SC
GROUP BY Sno;
【例3.90】将Student表中所有女生记录定义为一个视图。
CREATE VIEW F_Student(F_Sno,name,sex,age,dept)
AS
SELECT * /*没有不指定属性列*/
FROM Student
WHERE Ssex = '女';
2.删除视图
【例3.91】删除视图BT_S和IS_S1。
DROP VIEW BT_S; /*成功执行*/
DROP VIEW IS_S1; /*拒绝执行*/
/*要删除IS_S1,需使用级联删除*/
DROP VIEW IS_S1 CASCADE;
3.7.2 查询视图
【例3.92】在信息系学生的视图中找出年龄小于20岁的学生。
SELECT Sno,Sage
FROM IS_Student
WHERE Sage < 20;
SELECT Sno,Sage
FROM IS_Student
WHERE Sdept = 'IS' AND Sage < 20;
【例3.93】查询选修了1号课程的信息系学生。
SELECT IS_Student.Sno,Sname
FROM IS_Student,SC
WHERE
IS_Student.Sno = SC.Sno
AND SC.Cno = '1';
【例3.94】在S_G视图中查询平均成绩在90分以上的学生学号和平均成绩。
SELECT *
FROM S_G
WHERE Gavg >= 90;
CREATE VIEW S_G (Sno,Gavg)
AS
SELECT Sno,AVG(Grade)
FROM SC
GROUP BY Sno;
SELECT Sno,AVG(Grade)
FROM SC
WHERE AVG(Grade) >= 90
GROUP BY Sno;
SELECT Sno,AVG(Grade)
FROM SC
GROUP BY Sno
HAVING AVG(Grade) >= 90;
SELECT *
FROM (SELECT Sno,AVG(Grade)
FROM SC
GROUP BY Sno
) AS S_G(Sno,Gavg)
WHERE Gavg >= 90;
3.7.3 更新视图
【例3.95】将信息系学生视图IS_Student中学号”201215122”的学生姓名改为”刘辰”。
UPDATE IS_Student
SET Sname = '刘辰'
WHERE Sno = '201215122';
UPDATE Student
SET Sname = '刘辰'
WHERE
Sno = '201215122'
AND Sdept = 'IS';
【例3.96】向信息系学生视图IS_S中插入一个新的学生记录,其中学号为”201215129”,姓名为”赵新”,年龄为20岁。
INSERT
INTO IS_Student
VALUES('201215129','赵新',20);
INSERT
INTO Student(Sno,Sname,Sage,Sdept)
VALUES('200215129','赵新',20,'IS');
【例3.97】删除信息系学生视图IS_Student中学号为”201215129”的记录。
DELETE
FROM IS_Student
WHERE Sno = '201215129';
DELETE
FROM Student
WHERE
Sno = '201215129'
AND Sdept = 'IS';
不可更新的视图
UPDATE S_G
SET Gavg = 90
WHERE Sno = '201215121';
3.7.4 视图的作用
CREATE VIEW Student(Sno,Sname,Ssex,Sage,Sdept)
AS
SELECT SX.Sno,SX.Sname,SY.Ssex,SX.Sage,SY.Sdept
FROM SX,SY
WHERE SX.Sno = SY.Sno;
CREATE VIEW VMGRADE
AS
SELECT Sno, MAX(Grade) Mgrade
FROM SC
GROUP BY Sno;
SELECT SC.Sno,Cno
FROM SC,VMGRADE
WHERE
SC.Sno = VMGRADE.Sno
AND SC.Grade = VMGRADE.Mgrade;
第四章 数据库安全性
4.2 数据库安全性控制
4.2.4 授权:授予与收回
【例 4.1】把查询 Student 表权限授给用户 U1。
GRANT SELECT
ON TABLE Student
TO U1;
【例 4.2】把对 Student 表和 Course 表的全部权限授予用户 U2 和 U3。
GRANT ALL PRIVILIGES
ON TABLE Student,Course
TO U2,U3;
【例 4.3】把对表 SC 的查询权限授予所有用户。
GRANT SELECT
ON TABLE SC
TO PUBLIC;
【例 4.4】把查询 Student 表和修改学生学号的权限授给用户 U4。
GRANT UPDATE(Sno),SELECT
ON TABLE Student
TO U4;
【例 4.5】把对表 SC 的 INSERT 权限授予 U5 用户,并允许将此权限再授予其他用户。
GRANT INSERT
ON TABLE SC
TO U5
WITH GRANT OPTION;
【例 4.6】U5 可以将此权限授予 U6。
GRANT INSERT
ON TABLE SC
TO U6
WITH GRANT OPTION;
【例 4.7】U6 还可以将此权限授予U7。
GRANT INSERT
ON TABLE SC
TO U7;
【例 4.8】把用户 U4 修改学生学号的权限收回。
REVOKE UPDATE(Sno)
ON TABLE Student
FROM U4;
【例 4.9】收回所有用户对表 SC 的查询权限。
REVOKE SELECT
ON TABLE SC
FROM PUBLIC;
【例 4.10】把用户 U5 对 SC 表的 INSERT 权限收回。
REVOKE INSERT
ON TABLE SC
FROM U5 CASCADE;
4.2.5 数据库角色
【例 4.11】通过角色来实现将一组权限授予一个用户。
步骤如下:
① 首先创建一个角色 R1。
CREATE ROLE R1;
② 然后使用 GRANT 语句,使角色 R1 拥有 Student 表的 SELECT、UPDATE、INSERT 权限。
GRANT SELECT,UPDATE,INSERT
ON TABLE Student
TO R1;
③ 将这个角色授予王平,张明,赵玲。使他们具有角色 R1 所包含的全部权限。
GRANT R1
TO 王平,张明,赵玲;
④ 可以一次性通过 R1 来回收王平的这 3 个权限。
REVOKE R1
FROM 王平;
【例 4.12】角色的权限修改。
GRANT DELETE
ON TABLE Student
TO R1;
使角色 R1 在原来的基础上增加了 Student 表的 DELETE 权限。
【例 4.13】使 R1 减少 SELECT 权限。
REVOKE SELECT
ON TABLE Student
FROM R1;
4.3 视图机制
【例 4.14】建立计算机系学生的视图,把对该视图的 SELECT 权限授于王平,把该视图上的所有操作权限授于张明。
/* 先建立计算机系学生的视图 CS_Student */
CREATE VIEW CS_Student AS SELECT * FROM Student WHERE Sdept='CS';
/* 在视图上进一步定义存取权限 */
GRANT SELECT ON CS_Student TO 王平;
GRANT ALL PRIVILIGES ON CS_Student TO 张明;
4.4 审计
【例 4.15】对修改 SC 表结构或修改 SC 表数据的操作进行审计。
AUDIT ALTER,UPDATE
ON SC;
【例 4.16】取消对 SC 表的一切审计。
NO AUDIT ALTER,UPDATE
ON SC;
第五章 数据库完整性
5.1 实体完整性
5.1.1 定义实体完整性
【例 5.1】将 Student 表中的 Sno 属性定义为码。
CREATE TABLE Student
(Sno CHAR(9) PRIMARY KEY, /* 在列级定义主码 */
Sname CHAR(20) NOT NULL,
Ssex CHAR(2),
Sage SMALLINT,
Sdept CHAR(20)
);
CREATE TABLE Student
(Sno CHAR(9),
Sname CHAR(20) NOT NULL,
Ssex CHAR(2),
Sage SMALLINT,
Sdept CHAR(20),
PRIMARY KEY(Sno) /* 在表级定义主码 */
);
【例 5.2】将 SC 表中的 Sno、Cno 属性组定义为码。
CREATE TABLE SC
(Sno CHAR(9) NOT NULL,
Cno CHAR(4) NOT NULL,
Grade SMALLINT,
PRIMARY KEY (Sno,Cno) /*只能在表级定义主码*/
);
5.2 参照完整性
5.2.1 定义参照完整性
【例 5.3】定义 SC 中的参照完整性。
CREATE TABLE SC
(Sno CHAR(9) NOT NULL,
Cno CHAR(4) NOT NULL,
Grade SMALLINT,
PRIMARY KEY (Sno, Cno), /*在表级定义实体完整性*/
FOREIGN KEY (Sno) REFERENCES Student(Sno), /*在表级定义参照完整性*/
FOREIGN KEY (Cno) REFERENCES Course(Cno) /*在表级定义参照完整性*/
);
5.2.2 参照完整性检查和违约处理
【例 5.4】显式说明参照完整性的违约处理示例。
CREATE TABLE SC
(Sno CHAR(9) NOT NULL,
Cno CHAR(4) NOT NULL,
Grade SMALLINT,
PRIMARY KEY(Sno,Cno), /*在表级定义实体完整性,Sno,Cno 都不能取空值*/
FOREIGN KEY (Sno) REFERENCES Student(Sno) /*在表级定义参照完整性*/
ON DELETE CASCADE /*级联删除SC表中相应的元组*/
ON UPDATE CASCADE, /*级联更新SC表中相应的元组*/
FOREIGN KEY (Cno) REFERENCES Course(Cno) /*在表级定义参照完整性*/
ON DELETE NO ACTION /*当删除course 表中的元组造成与SC表不一致时拒绝删除*/
ON UPDATE CASCADE /*当更新course表中的cno时,级联更新SC表中相应的元组*/
);
5.3 用户定义的完整性
5.3.1 属性上的约束条件
【例 5.5】在定义 SC 表时,说明 Sno、Cno、Grade 属性不允许取空值。
CREATE TABLE SC
(Sno CHAR(9) NOT NULL,
Cno CHAR(4) NOT NULL,
Grade SMALLINT NOT NULL,
PRIMARY KEY (Sno, Cno), /*在表级定义实体完整性,隐含了Sno,Cno不允许取空值,
在列级不允许取空值的定义可不写 */
);
【例 5.6】建立部门表 DEPT,要求部门名称 Dname 列取值唯一,部门编号 Deptno 列为主码。
CREATE TABLE DEPT
(Deptno NUMERIC(2),
Dname CHAR(9) UNIQUE NOT NULL, /*要求Dname列值唯一, 并且不能取空值*/
Location CHAR(10),
PRIMARY KEY (Deptno)
);
【例 5.7】Student 表的 Ssex 只允许取“男”或“女” 。
CREATE TABLE Student
(Sno CHAR(9) PRIMARY KEY,
Sname CHAR(8) NOT NULL,
Ssex CHAR(2) CHECK (Ssex IN ('男', '女')), /*性别属性Ssex只允许取'男'或'女'*/
Sage SMALLINT,
Sdept CHAR(20)
);
【例 5.8】SC 表的 Grade 的值应该在 0 和 100 之间。
CREATE TABLE SC
(Sno CHAR(9),
Cno CHAR(4),
Grade SMALLINT CHECK (Grade>=0 AND Grade<=100), /*Grade取值范围是0到100*/
PRIMARY KEY (Sno,Cno),
FOREIGN KEY (Sno) REFERENCES Student(Sno),
FOREIGN KEY (Cno) REFERENCES Course(Cno)
);
5.3.2 元组上的约束条件
【例 5.9】当学生的性别是男时,其名字不能以 Ms. 打头。
CREATE TABLE Student
(Sno CHAR(9),
Sname CHAR(8) NOT NULL,
Ssex CHAR(2),
Sage SMALLINT,
Sdept CHAR(20),
PRIMARY KEY (Sno),
CHECK (Ssex='女' OR Sname NOT LIKE 'Ms.%')
); /*定义了元组中Sname和Ssex两个属性值之间的约束条件*/
5.4 完整性约束命名子句
【例 5.10】建立学生登记表 Student,要求学号在 90000~99999 之间,姓名不能取空值,年龄小于 30,性别只能是“男”或“女” 。
CREATE TABLE Student
(Sno NUMERIC(6)
CONSTRAINT C1 CHECK (Sno BETWEEN 90000 AND 99999),
Sname CHAR(20)
CONSTRAINT C2 NOT NULL,
Sage NUMERIC(3)
CONSTRAINT C3 CHECK (Sage<30),
Ssex CHAR(2)
CONSTRAINT C4 CHECK (Ssex IN ('男','女')),
CONSTRAINT StudentKey PRIMARY KEY(Sno)
);
【例 5.11】建立教师表 TEACHER,要求每个教师的应发工资不低于 3000 元。应发工资是工资列 Sal 与扣除项 Deduct 之和。
CREATE TABLE TEACHER
(Eno NUMERIC(4) PRIMARY KEY /*在列级定义主码*/
Ename CHAR(10),
Job CHAR(8),
Sal NUMERIC(7,2),
Deduct NUMERIC(7,2),
Deptno NUMERIC(2),
CONSTRAINT TEACHERFKey FOREIGN KEY(Deptno)
REFERENCES DEPT(Deptno),
CONSTRAINT C1 CHECK (Sal + Deduct >= 3000)
);
【例 5.12】去掉例 5.10 Student 表中对性别的限制。
ALTER TABLE Student DROP CONSTRAINT C4;
【例 5.13】修改表 Student 中的约束条件,要求学号改为在 900000~999999 之间,年龄由小于 30 改为小于 40。
/* 可以先删除原来的约束条件,再增加新的约束条件。 */
ALTER TABLE Student
DROP CONSTRAINT C1;
ALTER TABLE Student
ADD CONSTRAINT C1 CHECK (Sno BETWEEN 900000 AND 999999);
ALTER TABLE Student
DROP CONSTRAINT C3;
ALTER TABLE Student
ADD CONSTRAINT C3 CHECK(Sage < 40);
5.5 断言
【例 5.18】限制数据库课程最多 60 名学生选修。
CREATE ASSERTION ASSE_SC_DB_NUM
CHECK (60 >= (SELECT COUNT(*) /*此断言的谓词涉及聚集操作 count 的 SQL 语句*/
FROM Course,SC
WHERE
SC.Cno = Course.Cno
AND Course.Cname = '数据库'
)
);
【例 5.19】限制每一门课程最多 60 名学生选修。
CREATE ASSERTION ASSE_SC_CNUM1
CHECK(60 >= ALL (SELECT COUNT(*) /* 此断言的谓词,涉及聚集操作 count */
FROM SC /* 和分组函数 group by 的SQL语句 */
GROUP BY CNO
)
);
【例 5.20】限制每个学期每一门课程最多 60 名学生选修。
/* 首先修改 SC 表的模式,增加一个“学期(TERM)”的属性。 */
ALTER TABLE SC ADD TERM DATE;
/* 然后定义断言 */
CREATE ASSERTION ASSE_SC_CNUM2
CHECK (60 >= ALL (SELECT COUNT(*)
FROM SC
GROUP BY CNO,TERM
)
);
5.6 触发器
5.6.1 定义触发器
CREATE TRIGGER <触发器名> /*每当触发事件发生时,该触发器被激活*/
{BEFORE|AFTER} <触发事件> ON <表名> /*指明触发器激活的时间是在执行触发事件前或后*/
REFERENCING NEW|OLD ROWAS<变量> /* REFERENCING 指出引用的变量*/
FOR EACH {ROW|STATEMENT} /* 定义触发器的类型,指明动作体执行的频率*/
[WHEN <触发条件>] <触发动作体> /* 仅当触发条件为真时才执行触发动作体*/
【例 5.21】当对表 SC 的 Grade 属性进行修改时,若分数增加了10%,则将此次操作记录到另一个表中 SC_U(Sno,Cno,Oldgrade,Newgrade)中,其中 Oldgrade 是修改前的分数,Newgrade 是修改后的分数。
CREATE TRIGGER SC_T /* SC_T是触发器的名字*/
AFTER UPDATE OF Grade ON SC /* UPDATE OF Grade ON SC是触发事件 */
/* AFTER是触发的时机,表示当对 SC 的 Grade 属性修改完后再触发下面的规则 */
REFERENCING
OLDROW AS OldTuple,
NEWROW AS NewTuple
FOR EACH ROW /* 行级触发器,即每次执行一次 Grade 的更新,下面的规则就执行一次 */
WHEN (NewTuple.Grade >= 1.1*OldTuple.Grade) /* 触发条件,只有该条件为真时才执行 */
INSERT INTO SC_U(Sno,Cno,OldGrade,NewGrade) /* 下面的 INSERT 语句 */
VALUES(OldTuple.Sno,OldTuple.Cno,OldTuple.Grade,NewTuple.Grade)
【例 5.22】将每次对表 Student 的插入操作所增加的学生个数记录到表 StudentInsertLog 中。
CREATE TRIGGER Student_Count
AFTER INSERT ON Student /* 指明触发器激活的时间是在执行 INSERT 后 */
REFERENCING
NEW TABLE AS DELTA
FOR EACH STATEMENT /* 语句级触发器,即执行完 INSERT 语句后下面的触发动作体才执行一次 */
INSERT INTO StudentInsertLog (Numbers)
SELECT COUNT(*) FROM DELTA
【例 5.23】定义一个 BEFORE 行级触发器,为教师表 Teacher 定义完整性规则 “教授的工资不得低于 4000 元,如果低于 4000 元,自动改为 4000 元”。
CREATE TRIGGER Insert_Or_Update_Sal /*对教师表插入或更新时激活触发器*/
BEFORE INSERT OR UPDATE ON Teacher /* BEFORE触发事件*/
REFERENCING NEWrow AS NewTuple FOR EACH ROW /*行级触发器*/
BEGIN /*定义触发动作体,这是一个PL/SQL过程块*/
IF (newtuple.Job='教授') AND (newtuple.Sal < 4000)
THEN newtuple.Sal=4000;
END IF;
END; /*触发动作体结束*/