MySQL-存储过程-函数-触发器-游标

一、介绍

以往我们使用数据库都是基于SQL去完成数据的CRUD,但其实,SQL是可编程的,我们可以把他当做一门编程语言一样去使用它,在其中我们可以:声明变量,执行SQL,进行逻辑判断,循环等等操作。为了达成这个目标,我们需要学习 存储过程,函数等数据库对象的操作。

常见的数据库对象有:表,索引,视图,存储过程,函数,用户,触发器 等等。

本文基于 MySQL8 作为测试环境。

为了完成SQL的编程,我们需要先学习一些基础的语法:

1.1 变量

MySQL中变量分为三种类型: 系统变量、用户变量、局部变量。

1.1.1 系统变量

系统变量是MySQL数据库系统提供的,不是用户来定义的,属于服务器层面。分为全局变量(GLOBAL)、会话变量(SESSION)。

  • 全局变量:全局有效。
  • 会话变量:仅对当前会话有效。

查看变量的语法是:

-- 查询所有系统变量
show global variables;
show global variables like 'xx';  -- 模糊匹配

-- 查看指定的系统变量
select @@[global | session].变量名;
例如:
select @@global.autocommit; 
select @@session.autocommit;

1.1.2 用户变量

用户定义变量是用户根据需要自己定义的变量,使用用户变量时不需要提前声明,在用的时候直接用 @变量 使用就可以,其作用域为当前会话,即在会话1中定义的变量在会话2中是不能使用的。

-- 声明并赋值
set @变量名 =;    或者    set @变量名 :=;

-- 案例1:
set @number = 1; -- 为number变量设置值为1
select @number;  -- 查询出number的值

-- 案例2:把查询出来的数据赋值给变量
select 字段名 into @变量名 from 表名 where 条件;

1.1.3 局部变量

局部变量是用户根据需要自己定义的变量,仅在当前SQL代码局部有效(在后续存储过程中具体使用)。
MySQL 中可以使用 declare 关键字来定义局部变量,其基本语法如下:

declare 变量名 数据类型 default 默认值;

例如:declare age int(11) default 30; 

为变量赋值:

-- 方式1: 使用 SET 关键字来为变量赋值
declare a int default 0;
set a = 10;

-- 方式2:使用 select ... into 语句为变量赋值
declare a int default 0;
select age into a from user where id = 1;

1.2 if分支结构

单分支:

if(条件) then
    条件满足执行的语句
end if;

双分支:

if(条件) then
    条件满足执行的语句
else
    条件不满足执行的语句
end if;

多路分支:

if(条件1)  then 
    满足条件1执行的语句;
elseif(条件2) then
    满足条件2执行的语句;
else
    否则执行的语句;
end if;

1.3 case语句

case 语句也是用来进行条件判断

case case_value
	when1 then 当case_value和值1相等时执行的语句;
	when2 then 当case_value和值2相等时执行的语句;
	...
	else 当case_value和任何值都不相等时执行的语句;
end case;

1.4 whlie语句

while 条件 do
	满足条件执行的语句;
end while;

1.5 loop语句

loop也是循环结构,只不过他没有循环条件。

[begin_label:]loop
    循环执行的代码
end loop [end_label];
-- begin_label参数和 end_label参数分别表示循环开始和结束的标志,这两个标志必须相同,而且都可以省略。

-- 案例:循环对count+1(死循环)。
add_num:loop
    set @count=@count+1;
end loop add_num;

-- 停止loop需要使用leave,该案例表示,当count为100时停止循环。
add_num:loop
    set @count=@count+1;
    if @count=100 then
        leave add_num;  
end loop add_num;

1.6 leave 和 iterate

  • leave 结束循环
  • iterate 跳过本次循环进入下一次循环
  • 他们很类似于Java中的 breakcontinue

1.7 repeat语句

repeat语句是有条件控制的循环语句,每次语句执行完毕后,会对条件表达式进行判断,如果表达式返回值为 TRUE,则循环结束,否则重复执行循环中的语句。

[begin_label:] repeat
    statement_list
    until search_condition
end repeat[end_label]
  • begin_label和end_label 为 REPEAT 语句的标注名称,该参数可以省略
  • repeat语句内的语句被重复,直至until后的 search_condition 返回值为 TRUE,满足该条件时循环结束
  • statement_list 参数表示循环的执行语句
  • repeat循环都用 end repeat结束

案例:循环执行 count 加 1 的操作,count 值为 100 时结束循环。

repeat
    set @count=@count+1;
    until @count=100
end repeat;

二 、存储过程

存储过程:是一组为了完成特定功能的SQL语句的集合,一般用于报表统计,数据迁移等等。

准备测试数据:

DROP TABLE IF EXISTS student;
CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(50) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `score` double(4,1) DEFAULT NULL COMMENT '分数',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `student` VALUES (1, '张三', 18, 50.0);
INSERT INTO `student` VALUES (2, '李四', 20, 65.0);
INSERT INTO `student` VALUES (3, '王五', 20, 75.0);
INSERT INTO `student` VALUES (4, '赵六', 22, 85.0);

2.1 创建存储过程

存储过程命名建议:p_存储过程名

create procedure 存储过程名(参数列表)
begin
    封装的sql语句
end;

-- 存储过程名命名建议:p_存储过程名

注意:命令行下执行命令时,因为只要输了分号就表示语句结束了,所以,创建存储过程时需要执行命令:
delimiter $$ 表示开启一个代码段,在没有输入 $$ 时,表示整个代码段还未结束,就算输了分号也没事。

所以在命令行下应该这么写:

delimiter $$
create procedure 存储过程名(参数列表)
begin
    封装的sql语句(可以多条)
end;
$$

案例: 创建一个简单的存储过程,封装一条sql,不含参数

delimiter $$
create procedure p1()
begin
    select * from student;
end;
$$

2.2 查询存储过程

-- 查询所有存储过程的信息
show procedure status;
-- 查询指定存储过程的信息
show procedure status like '存储过程名';

-- 查询存储过程的创建详情
show create procedure 存储过程名;

2.3 调用存储过程

call 存储过程名(参数列表);

2.4 删除存储过程

drop procedure [if exists] 存储过程名;

2.5 创建含变量的存储过程

create procedure p2()
begin
    declare my_name varchar(50) default '';
    select name into my_name from student where id=2;
    select my_name;
end;        

其中语法说明:
    1. 声明变量:declare 变量名 变量类型 default 默认值
    2. into :  将值赋值给指定变量 

2.6 创建含参数的存储过程

案例1:IN 入参的使用

create procedure p3(IN my_id int)
begin
	declare my_name varchar(50) default '';
    select name into my_name from student where id=my_id;
    select my_name;
end;
-- 语法说明:IN 是关键字,表示入参的意思,即my_id用于接收用户传入的参数

案例2:OUT 出参的使用

-- 创建存储过程
create procedure p4(IN my_id int,out my_name varchar(50))
begin
    select name into my_name from student where id=my_id;
end;

-- 调用存储过程
call p4(2,@my_name);
-- 查询返回值
select @my_name;

案例3:INOUT 的使用,INOUT 修饰的变量既是入参,也是出参。

-- 创建存储过程。需求是:将传入的分数*0.5后返回
create procedure p5(INOUT score double)
begin
    set score := score * 0.5;
end;
-- 声明变量
set @score = 60;
-- 调用存储过程
call p5(@score);
-- 查看返回值
select @score; -- 结果为:30

2.7 创建带有if判断的存储过程

案例1:id为偶数查询对应的name值,id为奇数直接返回id值。

create procedure p6(IN my_id int)
begin
    declare my_name varchar(50) default '';
    if(my_id%2=0) then
        select name into my_name from student where id = my_id;
        select my_name;
    else
        select my_id;
    end if;
end;

案例2:根据用户id查询其成绩等级

-- 创建
create procedure p7(IN my_id int,OUT level varchar(50))
begin
	declare my_score int default 0;
    select score into my_score from student where id = my_id;
	if(my_score<60) 
		then set level = "不及格";
	elseif(my_score<80) 
		then set level = "良好";
	else
		set level = "优秀";
	end if;
end;
-- 调用
call p7(2,@level);
-- 获取等级
select @level;

三.函数

mysql中有内置函数,比如: now(),当然我们也可以自己封装!

3.1 创建函数

注意:mysql8在创建函数前需要执行如下设置,否则要报错。

set global log_bin_trust_function_creators=true;

创建函数的语法:

create function 函数名(参数列表) returns 返回值类型
begin
    执行语句;
    return ...;
end;

注意:函数必须有返回值!

案例:封装一个根据id查询name的自定义函数:

create function getName(my_id int) returns varchar(50)
begin
    declare my_name varchar(32) default '';
    select name into my_name from student where id=my_id;
    return my_name;
end;

3.2 执行函数

select 函数名(实参);

3.3 删除函数

drop function 函数名;

四.触发器

概念:根据对表的操作自动触发一些动作,触发条件可以是:insert,update,delete。

准备测试数据:

DROP TABLE IF EXISTS log;
CREATE TABLE `log` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `action` varchar(50) DEFAULT NULL COMMENT '动作(inset,update,delete)',
  `sid` int(11) DEFAULT NULL COMMENT '学员id',
  `time` datetime DEFAULT NULL COMMENT '时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

4.1 创建触发器

触发器命名建议:trg_触发器名

create trigger 触发器名 after 操作 on 表名 
for each row
begin
    触发后执行的语句;
end;

说明:

  • after 表示之后,也可以使用before 表示之前
  • 操作 on 表名 : 表示对哪个表做什么操作时触发,操作有:insert,update,delete
  • for each row: 表示行级别

案例:对student表插入数据之后,自动触发执行sql语句往log表中写入日志数据。

create trigger student_insert_trigger after insert on student
for each row
begin
    insert into log(action,sid,time) values('insert',NEW.id,now());
end;
-- 参数解释:NEW中包含的是新插入的数据,OLD 中包含老数据
-- 这的NEW.id就表示获取刚插入的学员数据的id主键

案例:创建触发器,在student表执行删除时,将删除的数据的id记录到log表中。

create trigger student_delete_trigger after delete on student
for each row
begin
    insert into log(action,sid,time) values('delete',OLD.id,now());
end;

4.2 查询触发器

show triggers;

4.3 删除触发器

drop trigger 触发器名;

五、游标

在 MySQL 中,存储过程或函数中的查询有时会返回多条记录,而使用简单的 SELECT 语句,没有办法得到第一行、下一行,这时可以使用游标来逐条读取查询结果集中的记录,游标一般配合循环使用。

在MySQL中,游标只能用于存储过程和函数。

5.1 游标相关命令

-- 创建游标
declare 游标名 cursor for 查询语句;

-- 打开游标
open 游标名;

-- 使用游标(读取结果集中的记录,并赋值给变量)
fetch 游标名 into 变量1,变量2,变量3...

-- 关闭游标
close 游标名;

5.2 游标案例

定义一个函数,查询出学生表中的所有记录,并拼接为字符串返回。

create function selectAllStudent() returns text
begin
	-- 定义变量用于存储学生表的数据
	declare my_id int default 0;
    declare my_name varchar(50) default "";
	declare my_age int default 0;
	declare my_score double default 0.0;
	-- 用户存储全部数据
	declare my_result text default "";
	-- 用于存储一行数据
	declare temp_str text default "";
	declare i int default 0;
	-- 声明变量用于存储student表总记录数
	declare my_count int default 0;
	-- 定义游标
	declare my_cursor cursor for select id,name,age,score from student;
	-- 查询总记录数
	SELECT COUNT(1) INTO my_count FROM student;
   -- 打开游标
	open my_cursor;
	-- 使用游标循环遍历结果集
	set i = 0;
	while i<my_count do
		-- 获取一行数据并赋值给变量
		fetch my_cursor into my_id,my_name,my_age,my_score;
		-- 将每一条数据拼到temp_str中
		select concat_ws(",",my_id,my_name,my_age,my_score) into temp_str;
		-- 将数据统一累加到my_result
		set my_result=concat_ws('|',my_result,temp_str);
		-- i累加
		set i = i+1;
	end while;
	-- 关闭游标
	close my_cursor;
	-- 返回结果
	return my_result;
end;

执行该函数会将表中数据拼为一个串返回,表中数据如下:

+----+------+------+-------+
| id | name | age  | score |
+----+------+------+-------+
|  1 | 张三 |   18 |  70.0 |
|  2 | 李四 |   20 |  80.0 |
|  3 | 王五 |   20 |  95.0 |
+----+------+------+-------+

执行结果:

|1,张三,18,70|2,李四,20,80|3,王五,20,95

你可能感兴趣的:(04-数据库,mysql,sql,数据库)