数据库之存储过程

存储过程

1.什么是存储过程?

存储过程就类似于python中的自定义的函数,它的内部包含了一系列可以执行的sql语句,存储过程存放于mysql服务端中,你可以直接通过调用存储过程触发内部sql语句的执行

2.存储过程的基本使用

procedure /prəˈsiːdʒə(r) 程序, 过程

-- 创建语法
delimiter //
create procedure 存储过程的名字([形参1,形参2,...])
begin
	sql代码
end //
delimiter ;

-- mysql 中调用存储过程语法
call 存储过程的名字([实参1,实参2,...]);

-- python中基于pymysql调用
cursor.callproc("存储过程的名字")
print(cursor.fetchall())

3.三种开发模式: 程序与数据库结合开发

# 第一种
"""  
应用程序: 程序员写业务代码开发
mysql: dba提前编写好存储过程,供应用程序调用
好处: 开发效率提升了,执行效率也上去了
缺点: 考虑到人为因素,需要跨部门沟通问题,后续的存储过程的扩展性差
"""

# 第二种
"""
应用程序: 程序员写代码开发之外 涉及到数据库操作也要自己手写
优点: 解决了跨部门不方便扩展的问题
缺点: 开发效率低,都是程序员干活.程序员不仅要会多种编程语言,还要考虑sql语句的执行效率问题
"""

# 第三种
"""
应用程序: 程序员只写程序代码 不写sql语句了.而是用别人写好的操作sql的框架即可
比如: 后面要讲的ORM框架对象关系映射
优点: 开发效率比前俩种开发效率都要高
缺点: 语句的扩展性差,可能会出现效率低下的问题.
"""
# 第一种基本不用,一般都是第三种方法,当出现效率低下的情况,再去手动的写一些sql语句

4.创建存储过程并调用(无参)

创建库并准备表和数据

create database procedure_test charset utf8;
use procedure_test;

CREATE TABLE blog (
    id INT PRIMARY KEY auto_increment,
    NAME CHAR (32),
    sub_time datetime
);

INSERT INTO blog (NAME, sub_time)
VALUES
    ('第1篇','2015-03-01 11:31:21'),
    ('第2篇','2015-03-11 16:31:21'),
    ('第3篇','2016-07-01 10:21:31'),
    ('第4篇','2016-07-22 09:23:21'),
    ('第5篇','2016-07-23 10:11:11'),
    ('第6篇','2016-07-25 11:21:31'),
    ('第7篇','2017-03-01 15:33:21'),
    ('第8篇','2017-03-01 17:32:21'),
    ('第9篇','2017-03-01 18:31:21');

mysql种创建及调用

delimiter //
create procedure p1()
begin
	select * from blog;
	insert into blog(name,sub_time) values('ldsb',now());
end //
delimiter ;

# 调用
call p1();

python中基于pymysql调用

import pymysql

conn = pymysql.connect(
	host = '127.0.0.1',
    port=3306,
    user='root',
    password='jzd123',
    database='procedure_test',
    charset='utf8'
)

cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

# 在python中 通过游标下的callproc方法调用存储过程p1
cursor.callproc('p1')
print(cursor.fetchall())

cursor.close()
conn.close()

5.创建存储过程并调用(有参)

对于存储过程, 可以接受参数,其参数有三类

in , out 和 inout

-- n n int      ---> 声明变量n为外部传入的值, 类型为int. 可以为存储过程定义的形参in直接传值,
-- out res int  ---> 声明变量res为返回值, 类型为int. 为存储过程定义的形参out传值, 需要实现使用set声明才能传.
-- inout x int  --->  声明变量x既能收值又能返回值. 虽然inout既能接收又能充当返回值, 但是为了保证int和out的传值的统一性, 因此也需要事先使用set声明才能传.
  • in: 传入参数
Copydelimiter //
create procedure p2(
    in n1 int,
    in n2 int
)
BEGIN
    select * from blog where id > n1;
END //
delimiter ;


# 在mysql中调用
call p2(3,2)

# 在python中基于pymysql调用
cursor.callproc('p2',(3,2))
print(cursor.fetchall())
  • out:返回值
Copydelimiter //
create procedure p3(
    in n1 int,
    out res int
)
BEGIN
    select * from blog where id > n1;
    set res = 1;
END //
delimiter ;

# 在mysql中调用
set @res=0;       # 定义全局变量用于给out定义的res形参. 0代表假(执行失败),1代表真(执行成功).
call p3(3,@res);  # 调用存储过程
select @res;      # 查看存储过程

# 在python中基于pymysql调用
cursor.callproc('p3',(3,0)) # 0相当于set @res=0
print(cursor.fetchall())    #查询select的查询结果

cursor.execute('select @_p3_0,@_p3_1;') # @p3_0代表第一个参数,@p3_1代表第二个参数,即返回值
print(cursor.fetchall()
  • inout: 既可以传入又可以返回
Copydelimiter //
create procedure p4(
    inout n1 int
)
BEGIN
    select * from blog where id > n1;
    set n1 = 1;
END //
delimiter ;

#在mysql中调用
set @x=3;
call p4(@x);
select @x;


# 在python中基于pymysql调用
cursor.callproc('p4',(3,))
print(cursor.fetchall()) # 查询select的查询结果

cursor.execute('select @_p4_0;') 
print(cursor.fetchall())

6. 执行存储过程: int+out

  • 创建库并准备表和数据
Copydrop database procedure_test;  # 小心
create database procedure_test charset utf8;
use procedure_test;

create table emp(
  id int not null unique auto_increment,
  name varchar(20) not null,
  sex enum('male','female') not null default 'male', #大部分是男的
  age int(3) unsigned not null default 28,
  hire_date date not null,
  post varchar(50),
  post_comment varchar(100),
  salary double(15,2),
  office int, #一个部门一个屋子
  depart_id int
);

#插入记录
#三个部门:教学,销售,运营
insert into emp(name,sex,age,hire_date,post,salary,office,depart_id) values
('jason','male',18,'20170301','张江第一帅形象代言',7300.33,401,1), #以下是教学部
('tom','male',78,'20150302','teacher',1000000.31,401,1),
('kevin','male',81,'20130305','teacher',8300,401,1),
('tony','male',73,'20140701','teacher',3500,401,1),
('owen','male',28,'20121101','teacher',2100,401,1),
('jack','female',18,'20110211','teacher',9000,401,1),
('jenny','male',18,'19000301','teacher',30000,401,1),
('sank','male',48,'20101111','teacher',10000,401,1),
('哈哈','female',48,'20150311','sale',3000.13,402,2),#以下是销售部门
('呵呵','female',38,'20101101','sale',2000.35,402,2),
('西西','female',18,'20110312','sale',1000.37,402,2),
('乐乐','female',18,'20160513','sale',3000.29,402,2),
('拉拉','female',28,'20170127','sale',4000.33,402,2),
('僧龙','male',28,'20160311','operation',10000.13,403,3), #以下是运营部门
('程咬金','male',18,'19970312','operation',20000,403,3),
('程咬银','female',18,'20130311','operation',19000,403,3),
('程咬铜','male',18,'20150411','operation',18000,403,3),
('程咬铁','female',18,'20140512','operation',17000,403,3);
  • mysql中创建及调用
Copydrop procedure p1;

# 定义存储过程
delimiter $$
create procedure p1(
    in m int,  # 该形参m只负责接收值, m不能当作参数返回。
    in n int,  
    out res int  # 该形参res只负责返回值,不能直接接收参数,需要使用set先定义变量。 针对out存储过程的返回值参数一定要设置一个变量, 通常用返回值判断你这天sql语句运行成功与否, 返回值为0通常代表成功
)
begin
    select tname from teacher where tid>m and tid<n;
    set res=0;  # 将res变量修改, 用来标识当前的存储过程代码确实执行了。(修改成啥都行)
end  $$
delimiter ;

# 定义变量
set @xxx=10;  # 争对形参res不能直接传数据,因该传一个变量命。

# 查看变量对应得值
set @xxx;

# 调用存储过程
"""
第一个第二个参数: 为存储过程in定义的参数传值
第二个参数: 为存储过程out定义的返回值传值, out需要使用set声明才能传
"""
call p1(1, 10, @xxx);

# 删除存储过程
drop procedure p1;
  • pymysql模块使用存储过程
Copyimport pymysql

conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='jzd123',
    database='procedure_test',
    charset='utf8',
)

cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

# 调用存储过程
cursor.callproc('p1', (1, 5, 10))
"""
pymysql底层实现. 
set @_p1_0 = 1
set @_p1_1 = 5
set @_p1_2 = 10
"""
print(cursor.fetchall())  

# 执行select语句验证
cursor.execute('select @_p1_0')  
print(cursor.fetchall())  # [{'@_p1_0:1'}]

cursor.close()
conn.close()

7. 删除存储过程

Copydrop procedure 存储过程的名字;
posted on 2021-02-27 22:23  Jkeykey  阅读(254)  评论(0编辑  收藏  举报