存储过程
(类似于其他编程语言的函数)
(学到现在发现 select 相当于其他编程语言里的 print)

不能修改存储过程,只能删除后重新创建;删除语法 drop procedure 存储过程名;
练习用的数据:
sql
CREATE TABLE dept(
deptno INT PRIMARY KEY,
dname VARCHAR(20),
loc VARCHAR(20)
);
INSERT INTO dept VALUES(10, '教研部','北京'),
(20, '学工部','上海'),
(30, '销售部','广州'),
(40, '财务部','武汉');
create table if not exists emp(
empno int primary key,
ename varchar(20),
job varchar(20),
mgr int,
hiredate date,
sal numeric(8,2), -- 与 decimal 类型具有相同的含义,用于存储精确的小数值。
comm numeric(8, 2), -- 该字段的总长度为 8,其中小数位数为 2
deptno int,
FOREIGN KEY (deptno) REFERENCES dept(deptno) ON DELETE SET NULL ON UPDATE CASCADE
);
INSERT INTO emp VALUES(1001, '甘宁', '文员', 1013, '2000-12-17', 8000.00, null, 20),
(1002, '黛绮丝', '销售员', 1006, '2001-02-20', 16000.00, 3000.00, 30),
(1003, '殷天正', '销售员', 1006, '2001-02-22', 12500.00, 5000.00, 30),
(1004, '刘备', '经理', 1009, '2001-4-02', 29750.00, null, 20),
(1005, '谢逊', '销售员', 1006, '2001-9-28', 12500.00, 14000.00, 30),
(1006, '关羽', '经理', 1009, '2001-05-01', 28500.00, null, 30),
(1007, '张飞', '经理', 1009, '2001-09-01', 24500.00, null, 10),
(1008, '诸葛亮', '分析师', 1004, '2007-04-19', 30000.00, null, 20),
(1009, '曾阿牛', '董事长', null, '2001-11-17', 50000.00, null, 10),
(1010, '韦一笑', '销售员', 1006, '2001-09-08', 15000.00, 0.00, 30),
(1011, '周泰', '文员', 1008, '2007-05-23', 11000.00, null, 20),
(1012, '程普', '文员', 1006, '2001-12-03', 9500.00, null, 30),
(1013, '庞统', '分析师', 1004, '2001-12-03', 30000.00, null, 20),
(1014, '黄盖', '文员', 1007, '2002-01-23', 13000.00, null, 10);
create table if not exists salgrade(
grade int primary key,
losal int,
hisal int
);
INSERT INTO salgrade VALUES(1,7000,12000),
(2,12010,14000),(3,14010,20000),(4,20010,30000);
定义变量
局部变量
default是设定默认值。

方式1:

方式2:

用户变量

系统变量



参数传递
in
练习:
1、 通过传入员工编号查询员工信息
2、 通过传入部门名和薪资,查询指定部门,并且薪资大于指定值的员工信息
sql
-- 通过传入员工编号查询员工信息
delimiter $$
create PROCEDURE proc03(in p_empno int)
begin
select * from emp where empno = p_empno;
end $$
delimiter ;
call proc03(1001);
-- 通过传入部门名和薪资,查询指定部门,并且薪资大于指定值的员工信息
delimiter $$
CREATE PROCEDURE proc04(IN p_dname VARCHAR(20),in p_sal int)
BEGIN
SELECT * FROM emp,dept WHERE emp.deptno=dept.deptno and dept.dname=p_dname and sal>p_sal;
END $$
delimiter ;
call proc04('销售部',13000);
out

sql
-- 封装有参数的存储过程,传入员工编号,返回员工名字和薪资
delimiter $$
CREATE PROCEDURE proc05(IN p_empno INT,OUT p_ename VARCHAR(20),OUT p_sal INT)
BEGIN
SELECT ename,sal into p_ename,p_sal FROM emp WHERE empno=p_empno;
END $$
delimiter ;
call proc05(1001,@o_ename,@o_sal);
SELECT @o_ename,@o_sal;
inout

sql
-- 传入员工名,拼接部门号,传入薪资,求出年薪
delimiter $$
CREATE PROCEDURE proc06(inout p_ename VARCHAR(20),inout p_sal int)
BEGIN
select concat(deptno,'_',ename) into p_ename from emp where ename = p_ename;
set p_sal=p_sal*12;
end $$
delimiter ;
set @o_ename = '关羽';
set @o_sal = 3000;
call proc06(@o_ename,@o_sal);
SELECT @o_ename,@o_sal;
流程控制
分支语句
if

练习:
-- 输入学生的成绩,来判断成绩的级别:
score<60:不及格
score >= 60,score<80:及格
score >= 80,score < 90 :良好
score >= 90,score <= 100:优秀
score>100:成绩错误
sql
delimiter $$
create procedure proc07_if(in in_score int)
begin
if in_score <60
then select '不及格';
elseif in_score <80
then select '及格';
elseif in_score<90
then select '良好';
elseif in_score<=100
then select '优秀';
else
select '成绩错误';
end if;
end $$
delimiter ;
call proc07_if(120);
-- 输入员工的名字,判断工资的情况。
sal<10000:试用薪资
sal >= 10000 and sal<20000:转正薪资
sal>=20000:元老薪资
sql
delimiter $$
create procedure proc08_if(in in_name varchar(20))
BEGIN
declare p_sal varchar(20);
declare state varchar(20);
select sal into p_sal from emp where ename=in_name;
if p_sal <10000
then set state = '试用薪资';
elseif p_sal <20000
then set state = '转正薪资';
ELSE
set state = '元老薪资';
end if;
select state;
end $$
delimiter ;
call proc08_if('张飞');
case

end case 结束要加分号;
1、支付方式:
1:微信支付、2:支付宝支付、3:银行卡支付、其他支付
2、 输入学生的成绩,来判断成绩的级别:
score<60:不及格
score >= 60,score<80:及格
score >= 80,score < 90 :良好
score >= 90,score <= 100:优秀
score>100:成绩错误
sql
delimiter $$
create procedure proc09_case(in type int)
begin
declare res varchar(20);
case type
when 1 then set res='微信支付';
when 2 then set res='支付宝支付';
when 3 then set res='银行卡支付';
else set res='其他支付';
end case;
select res;
end $$
delimiter ;
call proc09_case(2);
-- -------------------------------------------------------------
delimiter $$
create procedure proc10_case(in in_score int)
begin
case
when in_score <60
then select '不及格';
when in_score <80
then select '及格';
when in_score<90
then select '良好';
when in_score<=100
then select '优秀';
else
select '成绩错误';
end case;
end $$
delimiter ;
call proc10_case(120);
循环语句

while(常用)

数据准备:
sql
create table user(
uid int PRIMARY KEY,
uname varchar(50),
password varchar(50)
);
1、向表中循环添加五条数据
sql
delimiter $$
create procedure proc11_while(in in_count int)
begin
declare i int default 1;
label:while i<=in_count do
insert into user(uid,uname,password) values(i,concat('user-',i),'123456');
set i = i+1;
end while label;
end $$
delimiter ;
call proc11_while(5);
运行结果:

(用truncate table user;把表中的数据清除了再对这个表进行新的插入操作)
2、当加了五条数据就跳出循环
sql
delimiter $$
create procedure proc12_while(in in_count int)
begin
declare i int default 1;
label:while i<=in_count do
insert into user(uid,uname,password) values(i,concat('user-',i),'123456');
if i = 5 then
leave label;
end if;
set i = i+1;
end while label;
end $$
delimiter ;
call proc12_while(10);
运行结果:

3、跳过编号5的数据插入
sql
delimiter $$
create procedure proc13_while(in in_count int)
begin
declare i int default 0;
label:while i<in_count do
set i = i+1;
if i = 5 then
iterate label;
end if;
insert into user(uid,uname,password) values(i,concat('user-',i),'123456');
end while label;
end $$
delimiter ;

repeat
(类似于do...while...循环)

1、向表中循环添加5条数据
sql
delimiter $$
create procedure proc14_repeat(in cnt int)
begin
declare i int default 1;
REPEAT
insert into user(uid,uname,password) values(i,concat('user-',i),'645645');
set i=i+1;
UNTIL i>cnt
END REPEAT;
end $$
delimiter ;
call proc14_repeat(5);
loop

1、向表中循环添加5条数据
sql
delimiter $$
create procedure proc15_loop(in cnt int)
begin
declare i int default 1;
label:loop
insert into user(uid,uname,password) values(i,concat('user-',i),'645645');
set i=i+1;
if i>cnt then
leave label;
end if;
END loop;
end $$
delimiter ;
call proc15_loop(5);
游标

用例:
需求:输入一个部门名,查询该部门员工的编号、名字、薪资,将查询的结果集添加游标。
sql
delimiter $$
CREATE PROCEDURE proc16_cursor(in in_dname varchar(20))
BEGIN
declare t_num int;
declare t_name varchar(20);
declare t_sal decimal(8,2);
-- 声明游标
declare my_cursor cursor for
select empno,ename,sal from dept a,emp b where a.deptno=b.deptno and a.dname= in_dname;
-- 打开游标
open my_cursor;
label: LOOP
-- 取值
fetch my_cursor into t_num,t_name,t_sal;
select t_num,t_name,t_sal;
END LOOP label;
-- 关闭游标
close my_cursor;
END $$
delimiter ;
call proc16_cursor('销售部');
运行结果:(运行一次fetch查询结果表中的一行,所以有这么多结果)

按理来说,我们写的这个循环的一个死循环,但是它没有一直循环下去,而是在最后报错了,就是下图的情况。这和游标的读取方式有关,它是在表中一个一个往下查的,到查不到的时候就报错了。

异常处理
对于上面这种报错情况,就需要用到异常处理了。

大概就是你在写的时候遇到报错的一些情况之后,你来安排一下接下来怎么办,可以使代码顺利运行完成。

针对上面游标的情况,增加一个异常处理来进行优化:
sql
delimiter $$
CREATE PROCEDURE proc17_handler(in in_dname varchar(20))
BEGIN
-- 定义局部变量
declare t_num int;
declare t_name varchar(20);
declare t_sal decimal(8,2);
-- 定义标记值
declare flag int default 1;
-- 声明游标
declare my_cursor cursor for
select empno,ename,sal from dept a,emp b where a.deptno=b.deptno and a.dname= in_dname;
-- 定义句柄:异常处理方式
/*
1:异常处理完之后程序该怎么执行continue:继续执行剩余代码exi计:直接终止程序undo:不支持
2:触发条件
条件码:
如1329
条件名:
SQLWARNING
NOT FOUND
SQLEXCEPTION
3:异常触发之后执行什么代码
*/
declare continue handler for 1329 set flag=0;
-- 打开游标
open my_cursor;
label: LOOP
-- 取值
fetch my_cursor into t_num,t_name,t_sal;
if flag=1
then select t_num,t_name,t_sal;
else
leave label;
end if;
END LOOP label;
-- 关闭游标
close my_cursor;
END $$
delimiter ;
call proc17_handler('销售部');
运行结果这个时候就不会有报错了:
