SchoolWork-LaTeX/数据库系统原理与实践/平时作业/第六次作业.tex

527 lines
17 KiB
TeX
Raw Permalink Normal View History

\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}