前言
MySQL 5.0 版本开始支持存储过程。
存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。
存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。
优点
- 存储过程可封装,并隐藏复杂的商业逻辑。
- 存储过程可以回传值,并可以接受参数。
- 存储过程无法使用 SELECT 指令来运行,因为它是子程序,与查看表,数据表或用户定义函数不同。
- 存储过程可以用在数据检验,强制实行商业逻辑等。
缺点
- 存储过程,往往定制化于特定的数据库上,因为支持的编程语言不同。当切换到其他厂商的数据库系统时,需要重写原有的存储过程。
- 存储过程的性能调校与撰写,受限于各种数据库系统。
第一个例子
我们通过一个简单的例子来学习存储过程的基本语法 基本语法:
CREATE PROCEDURE produce_name(参数列表)
begin
end
案例: 先说几个问题,我这里用的是Navicat。Navicat中默认的语句结束符号是“;” 但是在存储过程中我们可能会有多条语句,每条语句的结束都是“;”,为了防止创建存储过程中Navicat就使用“;”作为结束符号,我们需要重新申明结束符号:
DELIMITER $$
或
DELIMITER
tips : 当然这个符号你可以自己定义(不要太随意就行)。 来吧!看看案例:
DELIMITER $$
DROP PROCEDURE IF EXISTS proc_bookcount;
CREATE PROCEDURE proc_bookcount()
BEGIN
DECLARE v_count int;
select count(*) into v_count from g_book;
select v_count;
END $$
DELIMITER ;
执行之后查看左边的结构视口:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jRIr1EJG-1632642140911)(MySQL-存储过程.assets/image-20210926085606454.png)]
执行存储过程:
CALL proc_bookcount;
删除存储过程:
DROP PROCEDURE proc_bookcount;
当然我们上面的存储过程仅仅是执行了一条查询语句。 存储过程中可以有业务逻辑,那么就需要基本的流程控制。 所以我们要写出一定质量的存储过程,就需要了解存储过程中的一些基本语法。 tips:只要你学过编程,这些都很简单。 当然,让我们觉得恶心的就是,每个不同的数据库中的存储过程的语法都不一样。这也是前面说过的存储过程最大的缺点。
基本语法
变量
MySQL的变量有:全局变量,会话变量,用户变量,系统变量,局部变量。 我们这里重点研究存储过程的使用,暂时只说局部变量。 局部变量就是申明在BEGIN和END之间的变量,作用域也就是在BEGIN和END之间。 申明变量:使用DECLARE关键字
DECLARE var_name var_type [default_value]
案例:
DELIMITER $$
CREATE PROCEDURE proc_bookcount()
BEGIN
DECLARE v_isbn varchar(255) default '9787111573319';
DECLARE v_title varchar(255);
set v_isbn = '9787302444541';
SELECT title into v_title from g_book where isbn = v_isbn;
select v_isbn,v_title;
END $$
DELIMITER ;
变量申明:
DECLARE v_isbn varchar;
给变量赋值有两种方式: 方式1:直接set
set v_bookid = 1;
方式2: 使用select into
select count(*) into v_count from book
tips:select后面的列必须和变量对应起来。
比如:
select isbn,title into v_isbn,v_title from book where booid = 1
案例:
CREATE PROCEDURE proc_bookcount()
BEGIN
DECLARE v_isbn varchar(255) default '9787111573319';
DECLARE v_title varchar(255);
set v_isbn = '9787302444541';
SELECT isbn,title into v_isbn,v_title from g_book where isbn = v_isbn;
select v_isbn,v_title;
END $$
流程控制语句
分支语句
[1] if…then …else 语法
if 条件 then
执的语句
end if;
if 条件 then
if语句
else
else语句
end if;
if 条件 then
if语句
elseif 条件 then
语句。。。
。。。。。
else
else语句
end if;
案例:
DELIMITER $$
create PROCEDURE proc_demo()
begin
declare v_num int;
declare v_result varchar(100) default '奇数';
set v_num = CEIL(RAND() * 100);
IF mod(v_num , 2) = 0 THEN
set v_result = '偶数';
end if;
select CONCAT('数字',v_num,'是',v_result);
end $$
DELIMITER ;
DELIMITER $$
create PROCEDURE proc_demo()
begin
declare v_num int;
declare v_result varchar(100);
set v_num = CEIL(RAND() * 100);
IF mod(v_num , 2) = 0 THEN
set v_result = '偶数';
ELSE
set v_result = '奇数';
end if;
select CONCAT('数字',v_num,'是',v_result);
end $$
DELIMITER ;
DELIMITER $$
create PROCEDURE proc_demo()
begin
declare v_num int;
declare v_result varchar(100);
set v_num = CEIL(RAND() * 100);
IF mod(v_num , 2) = 0 THEN
set v_result = '偶数';
elseif mod(v_num , 3) THEN
set v_result = '3的倍数';
ELSE
set v_result = '奇数';
end if;
select CONCAT('数字',v_num,'是',v_result);
end $$
DELIMITER ;
调用上面的过程
CALL proc_demo;
[2]CASE结构 直接上菜吧:
DELIMITER $$
CREATE PROCEDURE PROC_DEMO()
BEGIN
DECLARE v_num int;
set v_num = ROUND(RAND() * 10);
CASE v_num
when MOD(v_num,2)=0 THEN
select '偶数';
when MOD(v_num,3)=0 THEN
select '3的倍数';
else
select '奇数';
end case;
END $$
DELIMITER ;
循环语句
[1]while ···· end while
while 条件 do
end while
DELIMITER $$
CREATE PROCEDURE PROC_DEMO()
BEGIN
DECLARE v_num int default 0;
while v_num < 10 do
insert into temp values(v_num);
set v_num = v_num + 1;
end while;
END $$
DELIMITER ;
[2]repeat···· end repeat
repeat
until 循环条件
end repeat;
DELIMITER $$
create PROCEDURE proc_demo()
begin
declare v_num int default 0;
repeat
insert into temp values(v_num);
set v_num = v_num + 1;
until v_num >=10 end repeat;
end $$
DELIMITER ;
[3]loop ·····endloop
loop 循环不需要初始条件,这点和 while 循环相似,同时和 repeat 循环一样不需要结束条件, leave 语句的意义是离开循环。
DELIMITER $$
create PROCEDURE proc_demo()
begin
declare v_num int default 10;
loop_lable:loop
insert into temp values(v_num);
set v_num = v_num + 1;
if v_num >= 20 THEN
leave loop_lable;
end if;
end loop;
end $$
DELIMITER ;
使用loop实现一个双层循环:
DELIMITER $$
create PROCEDURE proc_demo()
begin
declare v_num int default 0;
declare v_inner_number int default 0;
loop_lable:loop
set v_inner_number = 0;
loop_inner_lable:loop
insert into temp values(CONCAT(v_num,v_inner_number));
set v_inner_number = v_inner_number + 1;
if v_inner_number >= 5 THEN
leave loop_inner_lable;
end if;
end loop;
set v_num = v_num + 1;
if v_num >= 3 THEN
leave loop_lable;
end if;
end loop;
end $$
DELIMITER ;
存储过程的参数
MySQL存储过程的参数用在存储过程的定义,共有三种参数类型,IN,OUT,INOUT,形式如:
CREAT EPROCEDURE 存储过程名([[IN |OUT |INOUT ] 参数名 数据类形...])
- IN 输入参数:表示调用者向过程传入值(传入值可以是字面量或变量)
- OUT 输出参数:表示过程向调用者传出值(可以返回多个值)(传出值只能是变量)
- INOUT 输入输出参数:既表示调用者向过程传入值,又表示过程向调用者传出值(值只能是变量)
1、in 输入参数
就简单理解为java方法的参数 为了确保我们的数据表结构不外漏,所以我们准备一个存储添加数据
DELIMITER $$
create procedure proc_addbook(in p_isbn varchar(255),in p_title varchar(255),in p_cost float,p_price float,p_pid varchar(10),p_category int)
BEGIN
insert into g_book values(p_isbn,p_title,p_cost,p_price,p_pid,p_category);
END $$
DELIMITER ;
额~~~~ 调用有输入参数的存储过程就需要传入对应的参数
call proc_addbook('95286589','一个废铁的练成史',56.3,185,'P005',1)
2、out输出参数
就简单的理解为java中方法的返回值
DELIMITER $$
create PROCEDURE proc_countbypublisher(in p_pname varchar(255),out p_count int)
BEGIN
select count(*) into p_count from g_book where pid = (select pid from g_pubsher where pname = p_pname);
end $$
DELIMITER ;
调用有输出参数的存储过程
set @count_out=0;
call proc_countbypublisher('机械工业出版社',@count_out);
select @count_out '机械工业出版社的图书数量';
关于用户变量的说明:
上面的调用过程中,我们使用了用户变量 @count_out
用户变量都是以@开头,默认是在一次连接中有效的。
3、inout输入参数
额!!!就字面意思。既能输入,也能输出。 其实知道就好,尽量不要使用这种类型的参数
DELIMITER $$
create PROCEDURE proc_countbypublisher1(inout param varchar(255))
BEGIN
select count(*) into param from g_book where pid = (select pid from g_pubsher where pname = param);
end $$
DELIMITER ;
调用
set @param = '机械工业出版社';
call proc_countbypublisher1(@param);
select @param '机械工业出版社的图书数量'
JDBC调用存储过程
准备一个分页查询的存储过程
在这个存储过程中需要执行字符串形式的sql,需要用户变量。
DELIMITER $$
create PROCEDURE proc_page(tableName varchar(255),pageNum int,pageSize int,term varchar(255),out total int)
BEGIN
declare startIndex int default 0;
declare done int default 0;
set @v_total = 1;
set @v_total_sql = concat('select count(*) into @v_total from ',tableName);
if term!='' THEN
set @v_total_sql = CONCAT(@v_total_sql,' where ',term);
end if;
prepare s0 from @v_total_sql;
execute s0;
deallocate prepare s0;
set total = @v_total;
set @v_sql = CONCAT('select * from ',tableName);
if term!='' THEN
set @v_sql = CONCAT(@v_sql,' where ',term);
end if;
set startIndex = (pageNum-1)*pageSize;
set @v_sql = CONCAT(@v_sql,' limit ',startIndex,',',pageSize);
prepare s1 from @v_sql;
execute s1;
deallocate prepare s1;
end $$
DELIMITER ;
drop PROCEDURE proc_page
set @out_total = 0;
call proc_page('g_book',1,3,'',@out_total);
select @out_total;
JDBC调用存储过程:
package com.st.dao;
import java.sql.*;
import java.util.concurrent.Callable;
public class ProcTestDAO extends BaseDAO{
public void queryPaperByProc() throws SQLException {
Connection con = getCon();
CallableStatement cst = con.prepareCall("{call proc_page(?,?,?,?,?)}");
cst.setString(1,"paper");
cst.setInt(2,2);
cst.setInt(3,5);
cst.setString(4,"paper_title like '%问卷%'");
cst.registerOutParameter(5, Types.INTEGER);
ResultSet rs = cst.executeQuery();
int total = cst.getInt(5);
System.out.println("总体条数:"+total);
while(rs.next()){
System.out.println(rs.getString("paper_title"));
};
}
public static void main(String[] args) {
try {
new ProcTestDAO().queryPaperByProc();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
|