@@ -0,0 +1,527 @@
\documentclass [全部作业] { subfiles}
\input { mysubpreamble}
% 不知道为什么这里又变成不用framed才能不出现换页问题了
% \BeforeBeginEnvironment{minted}{}
% \AfterEndEnvironment{minted}{}
% 这样好像不能替换掉原来的,那就只能把framed移动到需要的部分里了
\begin { document}
\setcounter { chapter} { 5}
\chapter { 第六次作业}
\begin { enumerate}
\item \choice [1] { 关于游标,选项中说法错误的是()
A
游标可以定在查询结果集的特定行,也可以从结果集的当前行检索一行或多行
B
通常我们并不使用游标,但是需要逐条处理数据的时候,游标显得十分重要
C
游标使用时必须在完成后关闭,以释放资源
D
游标的 SELECT 语句中可以使用 INTO 子句来创建新表
} { 4}
\item \choice [1] { 下列关于触发器的描述正确的是()
A
MySql的触发器只支持行级出发,不支持语句级触发
B
触发器可以调用将数据返回客户端的存储程序
C
在触发器中可以使用显式或者隐式方式开始或结束事务的语句
D
在MySql中,不可使用new和old引用触发器中发生的记录内容
} { 1}
\item \choice [1] { 关于before和after触发器的说法中,哪一项是正确的?()
A
在 BEFORE 触发器中,可以对 INSERT 和 UPDATE 的 NEW 值进行修改;而在 AFTER 触发器中,无法修改 NEW 值
B
在 AFTER 触发器中,可以修改 NEW 值,但在 BEFORE 触发器中不能修改
C
BEFORE 触发器不能用于 UPDATE 操作
D
AFTER 触发器可以对 INSERT 和 UPDATE 的 NEW 值进行修改
} { 1}
\item \choice [1] { 关于 MySQL 触发器中 SELECT 语句的使用,以下哪种说法是正确的?()
A
触发器中可以包含 SELECT 语句,并且可以返回结果集
B
触发器中可以包含 SELECT 语句,但不能返回结果集
C
触发器中必须包含 SELECT 语句,以便执行其他操作
D
触发器中可以使用 SELECT 语句来更新表的记录
} { 2}
\item \choice [1] { 关于存储过程的说法中,哪一项是正确的?()
A
存储过程可以通过 SELECT 语句直接返回结果集给调用者
B
存储过程只能接受一个输入参数,并且不能返回任何值
C
存储过程可以使用 BEGIN ... END 块来定义多个 SQL 语句的执行逻辑
D
存储过程的定义必须包含 CREATE FUNCTION 关键字
} { 3}
\questionandanswer [] {
创建一个名为 check_ student_ age_ trigger 的触发器,该触发器用于在 students 表中插入新记录之前进行年龄检查。要求如下:
触发器在执行 INSERT 操作之前触发。
如果新插入的学生年龄(age 列)小于 18 岁,触发器应该将 age 设置为 18。
如果新插入的学生年龄大于 120 岁,触发器应该将 age 设置为 120。
触发器应该记录所有插入操作的原始年龄值到一个名为 age_ log 的表中,表结构如下:
log_ id: INT, 主键,自增
student_ id: INT, 学生学号
original_ age: INT,原始年龄值
log_ time: DATETIME,记录插入的时间
要求:
( 1)编写 SQL 语句创建 age_ log 表。
( 2)编写 SQL 语句创建 check_ student_ age_ trigger 触发器。
提示:
使用 NEW 关键字访问将要插入的记录中的字段。
使用 INSERT INTO 语句将原始年龄记录到 age_ log 表中。
示例:
如果插入一条记录:INSERT INTO students (name, age) VALUES ('Alice', 16);
触发器将插入 age 为 18,并在 age_ log 表中记录 original_ age 为 16。
} { }
{ \kaishu
\begin { minted} { SQL}
create table age_ log
(
log_ id int primary key auto_ increment,
student_ id int comment '学生学号',
original_ age int comment '原始年龄值',
log_ time datetime comment '记录插入的时间'
);
create trigger check_ student_ age_ trigger
before insert
on students
for each row
begin
insert into age_ log(student_ id, original_ age, log_ time) value (new.id, new.age, now());
case
when (new.age < 18) then set new.age = 18;
when (new.age > 120) then set new.age = 120;
end case;
end;
\end { minted}
}
\begin { verification}
{ \small
\begin { minted} { SQL}
drop table if exists students;
drop table if exists age_ log;
create table students
(
id int primary key,
name varchar(100) comment '姓名',
age int comment '年龄'
);
create table age_ log
(
log_ id int primary key auto_ increment,
student_ id int comment '学生学号',
original_ age int comment '原始年龄值',
log_ time datetime comment '记录插入的时间'
);
create trigger check_ student_ age_ trigger
before insert
on students
for each row
begin
insert into age_ log(student_ id, original_ age, log_ time) value (new.id, new.age, now());
case
when (new.age < 18) then set new.age = 18;
when (new.age > 120) then set new.age = 120;
end case;
end;
insert into students(id, name, age)
values (10001, 'Alice', 16),
(10002, 'Bob', 150);
\end { minted}
}
students:
\begin { csv}
,id,name,age
1,10001,Alice,18
2,10002,Bob,120
\end { csv}
age_ log:
\begin { csv}
,log_ id,student_ id,original_ age,log_ time
1,1,10001,16,2024-11-01 15:39:52
2,2,10002,150,2024-11-01 15:39:52
\end { csv}
\end { verification}
\questionandanswer [] {
假设有以下两个表:
\noindent \includegraphics [width=1\linewidth] { imgs/2024-11-01-16-47-16.png}
创建一个名为 withdraw_ funds 的存储过程,用于从银行账户中提款。要求如下:
存储过程接受两个参数:
account_ id: INT, 要提款的账户的 ID
amount: DECIMAL(10, 2), 要提款的金额
在提款前检查该账户的余额是否足够支付提款金额。
如果余额不足,存储过程应返回一个错误消息。
如果提款成功,更新账户余额,并在 transactions 表中记录提款信息(转出账户为提款账户,转入账户为 NULL,金额为提款金额)。
} { }
{ \kaishu
\begin { minted} { SQL}
create procedure withdraw_ funds(in _ account_ id int, in amount decimal(10, 2))
begin
start transaction;
-- 如果账户不存在呢?好像没有交代应该怎么处理
if (select accounts.balance from accounts where accounts.account_ id = _ account_ id) < amount then
rollback ;
signal sqlstate '45000' set message_ text = '余额不足';
end if;
update accounts set balance = balance - amount where accounts.account_ id = _ account_ id;
insert into transactions(from_ account_ id, to_ account_ id, amount) value (_ account_ id, null, amount);
commit;
end;
\end { minted}
}
\begin { verification}
{ \small
\begin { minted} { SQL}
drop table if exists accounts;
drop table if exists transactions;
create table accounts
(
-- 自增好像是从1开始的
account_ id int primary key auto_ increment comment '账户唯一标识符',
account_ holder varchar(100) comment '账户持有者的姓名',
balance decimal(10, 2) comment '账户余额'
);
create table transactions
(
transaction_ id int primary key auto_ increment comment '交易的唯一标识符',
from_ account_ id int comment '转出账户的ID',
to_ account_ id int comment '转入账户的ID',
amount decimal(10, 2) comment '转账金额',
transaction_ time datetime default current_ timestamp comment '转账时间'
);
insert into accounts(account_ holder, balance)
values ('a', 10),
('b', 100);
drop procedure if exists withdraw_ funds;
create procedure withdraw_ funds(in _ account_ id int, in amount decimal(10, 2))
begin
start transaction;
-- 如果账户不存在呢?好像没有交代应该怎么处理
if (select accounts.balance from accounts where accounts.account_ id = _ account_ id) < amount then
rollback ;
signal sqlstate '45000' set message_ text = '余额不足';
end if;
update accounts set balance = balance - amount where accounts.account_ id = _ account_ id;
insert into transactions(from_ account_ id, to_ account_ id, amount) value (_ account_ id, null, amount);
commit;
end;
call withdraw_ funds(2, 10);
call withdraw_ funds(1, 10);
call withdraw_ funds(1, 10);
\end { minted}
}
\begin { minted} { text}
[2024-11-01 16:35:33] [45000][1644] 余额不足
\end { minted}
accounts:
\begin { csv}
,account_ id,account_ holder,balance
1,1,a,0.00
2,2,b,90.00
\end { csv}
\vspace { 1em}
transactions:\vspace { 1em} \\
\small \begin { csv}
,transaction_ id,from_ account_ id,to_ account_ id,amount,transaction_ time
1,1,2,,10.00,2024-11-01 16:35:33
2,2,1,,10.00,2024-11-01 16:35:34
\end { csv}
\end { verification}
\questionandanswer [] {
请根据下图所示的银行数据库,编写一个触发器来执行下列操作:在删除一个账户时,检查该账户的拥有者是否还有其他账户,如果没有,则将其从depositor关系中删除。
\includegraphics [width=1\linewidth] { imgs/2024-11-01-21-57-55.png}
} { }
{ \kaishu
这里为什么要检测是否有其他账户,删除一个账户时不管这个人有没有其他账户都需要从depositor里删除account_ number对应的行吧,那删除最后一个账户时depositor里自然也删完了。
\begin { minted} { SQL}
create trigger delete_ account
after delete
on account
for each row
begin
delete from depositor where account_ number = old.account_ number;
end;
\end { minted}
}
\begin { verification}
\begin { minted} { SQL}
drop table if exists account;
drop table if exists depositor;
create table account
(
account_ number int primary key,
branch_ name varchar(100),
balance int
);
create table depositor
(
customer_ name varchar(100),
account_ number int references account.account_ number
);
insert into account
values (1, 'a', 100),
(2, 'b', 200);
insert into depositor
values ('c', 1),
('c', 2);
create trigger delete_ account
after delete
on account
for each row
begin
delete from depositor where account_ number = old.account_ number;
end;
delete from account where account_ number = 1;
select * from depositor;
\end { minted}
\begin { csv}
,customer_ name,account_ number
1,c,2
\end { csv}
\begin { minted} { SQL}
delete from account where account_ number = 2;
select * from depositor;
\end { minted}
\begin { csv}
,customer_ name,account_ number
,,
\end { csv}
\end { verification}
\questionandanswer [] {
假设有一个名为 employees 的员工表和一个名为 departments 的部门表,结构如下:
\includegraphics [width=1\linewidth] { imgs/2024-11-02-11-15-08.png}
(1)存储过程:名为 increase_ salary,用于根据部门名称增加员工工资。要求:
接受两个参数:dept_ name(部门名称)和 percentage(增加的百分比)。
如果部门不存在,返回错误消息。
如果部门存在,更新该部门所有员工的工资,并返回更新的员工数量。
} { }
{ \kaishu
\begin { minted} { SQL}
create procedure increase_ salary(in dept_ name varchar(100), in percentage int)
begin
if (select count(*) from departments where dept_ name = departments.department_ name) <= 0 then
signal sqlstate '45000' set message_ text = '部门不存在';
end if;
update employees join departments using (department_ id)
set salary = salary * (1 + percentage / 100)
where department_ name = dept_ name;
-- 存储过程怎么返回更新的员工数量
-- 30 ms 中有 2 行受到影响 返回的影响行数应该就能反映更新的员工数量了
end;
\end { minted}
}
\questionandanswer [] {
( 2)函数:名为 get_ average_ salary,用于获取指定部门的平均工资。要求:
接受一个参数:dept_ id(部门 ID)。
返回该部门员工的平均工资,如果没有员工,则返回 0。
} { }
{ \kaishu
\begin { minted} { SQL}
create function get_ average_ salary(dept_ id int) returns decimal(10, 2)
begin
declare result decimal(10, 2);
if (select count(*) from employees where department_ id = dept_ id) <= 0 then
set result = 0;
else
select avg(employees.salary) into result from employees where department_ id = dept_ id;
end if;
return result;
end;
\end { minted}
}
\begin { verification}
\begin { minted} { SQL}
drop table if exists employees;
drop table if exists departments;
drop procedure if exists increase_ salary;
drop function if exists get_ average_ salary;
create table employees
(
employee_ id int primary key auto_ increment comment '员工唯一标识符',
employee_ name varchar(100) comment '员工姓名',
department_ id int comment '部门ID',
salary decimal(10, 2) comment '员工工资'
);
create table departments
(
department_ id int primary key auto_ increment comment '部门唯一标识符',
department_ name varchar(100) comment '部门名称'
);
\end { minted}
\quad
% 大概知道了,只要在前一页结束前的位置加一个minted分段,并且在这个分段前面或后面存在一个字符,即使是空白的\quad 也可以,就不会出现分页异常的问题了
\begin { minted} { SQL}
create procedure increase_ salary(in dept_ name varchar(100), in percentage int)
begin
if (select count(*) from departments where dept_ name = departments.department_ name) <= 0 then
signal sqlstate '45000' set message_ text = '部门不存在';
end if;
update employees join departments using (department_ id)
set salary = salary * (1 + percentage / 100)
where department_ name = dept_ name;
-- 存储过程怎么返回更新的员工数量
-- 30 ms 中有 2 行受到影响 返回的影响行数应该就能反映更新的员工数量了
end;
create function get_ average_ salary(dept_ id int) returns decimal(10, 2)
begin
declare result decimal(10, 2);
if (select count(*) from employees where department_ id = dept_ id) <= 0 then
set result = 0;
else
select avg(employees.salary) into result from employees where department_ id = dept_ id;
end if;
return result;
end;
insert into departments
values (101, 'aaa'),
(102, 'bbb');
insert into employees(employee_ name, department_ id, salary)
values ('a', 101, 10),
('b', 101, 100),
('c', 102, 1000);
\end { minted}
\begin { minted} { SQL}
select get_ average_ salary(101);
\end { minted}
\begin { csv}
,get_ average_ salary(101)
1,55.00
\end { csv}
\begin { minted} { SQL}
select get_ average_ salary(103);
\end { minted}
\begin { csv}
,get_ average_ salary(103)
1,0.00
\end { csv}
\begin { minted} { SQL}
call increase_ salary('aaa', 50);
\end { minted}
\begin { minted} { text}
[2024-11-02 11:13:13] 30 ms 中有 2 行受到影响
\end { minted}
\begin { minted} { SQL}
select * from employees;
\end { minted}
\begin { csv}
,employee_ id,employee_ name,department_ id,salary
1,1,a,101,15.00
2,2,b,101,150.00
3,3,c,102,1000.00
\end { csv}
\begin { minted} { SQL}
call increase_ salary('ccc', 100);
\end { minted}
\begin { minted} { text}
[2024-11-02 11:22:51] [45000][1644] 部门不存在
\end { minted}
\end { verification}
\questionandanswer [] {
存储过程、函数和触发器的区别?
} {
存储过程不能使用return语句返回结果,只能通过传入参数设置为out来返回结果;而函数可以直接使用return语句返回结果。
触发器没有传入参数也不能返回结果,只能通过old和new获取更新前后的值。
}
\questionandanswer [] {
触发器的作用?Mysql表中允许有多少个触发器?
} {
触发器定义了一系列操作,这一系列操作称为触发程序,当触发事件发生时,触发程序会自动运行。
触发器主要用于监视某个表的插入(insert)、更新(update)和删除(delete)等更新操作,这些操作可以分别激活该表的insert、update和delete类型的触发程序运行,从而实现数据的自动维护。
在5.7.2版本以前,同一个表不能创建两个相同触发时间、触发事件的触发程序,那么就是6个触发器:
before insert、after insert、before update、after update、before delete、after delete。
在5.7.2版本之后没有此限制,那么对触发器的数量就没有限制了。
}
\end { enumerate}
\end { document}