MySQL存储过程与函数
一提到存储过程可能就会引出另一个话题就是存储过程的优缺点,这里也不做讨论。因为mysql中存储过程和函数的语法非常接近所以就放在一起,主要区别就是函数必须有返回值(return),并且函数的参只有IN类型而存储过程有IN、OUT、INOUT这三种类型。
7 min read
By
myfreax

一提到存储过程可能就会引出另一个话题就是存储过程的优缺点,这里也不做讨论。因为mysql中存储过程和函数的语法非常接近所以就放在一起,主要区别就是函数必须有返回值(return),并且函数的参数只有IN类型而存储过程有IN、OUT、INOUT这三种类型。
权限
创建存储子程序需要CREATE ROUTINE权限,移除存储子程序需要ALTER ROUTINE权限。这个权限自动授予子程序的创建者,执行子程序需要EXECUTE权限。然而,这个权限自动授予子程序的创建者。同样,子程序默认的SQL SECURITY 特征是DEFINER,它允许用该子程序访问数据库的用户与执行子程序联系到一起
mysql函数即使重启mysqld服务后函数依然会存在,只存在指定的数据库,不会跨数据库
创建数据库
drop database if exists test_1;
create database test_1;
use test_1;
函数
第一个函数
delimiter $$ #定义结束符 $$
drop function if exists hello; #判断hello函数是否存在,如果存在则删除
create function test_1.hello (name char(20),age int(2)) returns char(225)
#sql 顾名思义 是创建函数的意思注意:
#sql 语言是强类型语言因此要声明参数的类型和返回值的类型
begin #函数体的开始
#set @i=0; #set关键字 声明一个全局变量@i sql也有局部和全局变量
declare greeting char(20);
#declare 关键字声明一个局部变量
#注意:函数内部不能同时有局部变量和全局变量的存在
declare num int(20);
declare str char(20);
declare restr char(225);
declare max int(20);
if hour(now()) < 12 then
set greeting ='早上好';
#set 关键字; 一般用来赋值,赋值给局部变量,全局变量一样
elseif hour(now()) >12 then
set greeting ='下午好';
end if; #end if这里一定要分开
if (age < 18) then #判断条件可以加上括号
set str='未成年';
else
set str='成年';
end if;
set num=0;
while num < age do
set num=age+1;
end while; #注意分开 end while
set max=0;
w:while age < 100 do
set age=age+1;
if age = 4 then
#leave w;
#leave 关键字,相当于break跳出循环w是指明关键字leave跳出那个循环的
iterate w; #iterate相当于continue跳过
end if;
set max=max+1;
end while w;
#select concat(name,greeting,'你的幸运数字是') into restr;
select concat(name,greeting,'你的幸运数字是',max,str) into restr;
#将运算后的结果赋值给restr
return restr;
end
$$
delimiter ; #还原mysql操作结束符
select test_1.hello('freax',12); #函数调用
创建一个自动生成订单序号的函数20140103001,可以减少连接数据库的次数,减少数据库的连接
drop function if exists create_sn;
delimiter $$
create function test_1.create_sn() returns bigint(15)
#编写程序时要注意数据类型
begin
declare order_sn bigint(15);
declare prev bigint(15);
declare prevdatetime bigint(15);
declare sn bigint(15);
declare nowdate bigint(15);
select order_sn from userorder order by userorder_id desc limit 1 into prev;#赋值prev
select concat(year(prev),month(prev),dayofmonth(prev)) into prevdatetime;
select right(prev,4) into sn;
select concat(year(now()),month(now()),dayofmonth(now())) into nowdate;
if isnull(prev) then
select concat(nowdate,'0001') into order_sn;
return order_sn;
elseif nowdate = prevdatetime then
select concat(nowdate,'0001') into order_sn;
return order_sn;
else
select concat(prevdatetime,(sn+1)) into order_sn;
return order_sn;
end if;
end
$$
delimiter ;
select create_sn();#函数调用
产生随机字符串,用于测试数据库
drop function if exists randstr;
delimiter $$
create function test_1.randstr(num int(11)) returns char(255)
#为了容易区分那个函数或者存储过程是那个数据库的
#可以在函数名中加上数据库前缀test_randstr;
begin
declare str char(255) default 'q1we23r4t5y6u7i8o9p0asdfghjklzxcvbnm';
declare nums int(11);
declare returnstr char(255);#SQL变量名不能和列名一样
declare i int(11) default 0;
select floor(truncate(rand(),1)*36)+1 into nums;
select substring(str,nums,1) into returnstr;
#declare i int(11) default 0;
#在声明变量时一定要在begin语句之后,除begin外的任何语句之前
while i <num do
select floor(truncate(rand(),1)*36)+1 into nums;
select concat(substring(str,nums,1),returnstr) into returnstr;
#set returnstr=concat(substring(str,nums,1),returnstr);
set i=i+1;
end while;
return returnstr;
end
$$
delimiter ;
drop function if exists ceshi;
delimiter $$
create function ceshi() returns char(255)
begin
declare ceshistr1 char(255);
declare ceshistr2 char(255);
declare ceshistr char(255);
#select order_sn,userorder_id from userorder limit 1 into ceshistr;
#ERROR 1222 (21000):
#The used SELECT statements have a different number of columns
select order_sn,userorder_id into ceshistr1,ceshistr2 from userorder limit 1 ;
#在mysql中一个列的数据必须占用一个变量,否则会出现上面的错误
select concat(ceshistr1,ceshistr2) into ceshistr;
#select * from userorder;
#存储函数的限制:不能再存储函数中返回整个表的数据
#ERROR 1415 (0A000): Not allowed to return a result set from a function
return ceshistr;
end
$$
delimiter ;
存储过程
第一个存储过程
drop procedure if exists simpleproc;
delimiter $$
CREATE PROCEDURE simpleproc (OUT param1 INT)
BEGIN
SELECT * FROM userorder;#存储过程可以返回整张表的数据
END
$$
delimiter ;
call simpleproc();#调用存储过程
存储过程学习声明变量
drop procedure if exists test2;
delimiter $$
create procedure test2 ()
begin
declare str char(255) default 'huangyanxiong';
#在存储过程声明局部变量并赋值
set @color='red';
#在存储过程中声明全局变量并赋值
#注意:函数内不能同时有局部变量和全局变量
#select * from userorder;
select @color as colors; 、
#一般采用这种方式输出到终端
end
$$
delimiter ;
call test2();
存储过程传递参数
drop procedure if exists test3;
delimiter $$
create procedure test3(in username char(50))
begin
select username as user_name;
end
$$
delimiter ;
call test3('huangyanxiong');
drop procedure if exists test4;
delimiter $$
create procedure test4(username char(50))
begin
declare str char(50);
select concat(username,'xxxxxx') into str;
select str as string;#设置别名返回
end;
$$
delimiter ;
call test4('huangyanxiong');
drop procedure if exists test5;
delimiter $$
create procedure test5 (username char(50)) #默认使用in
begin
set @age=12;
select username as usernames,@age as age;#使用同一张表返回
end
$$
delimiter ;
call test5('huangyanxiong');
把函数改变为存储过程很简单,改改就可以,把上面的订单序号改为存储过程
drop procedure if exists create_sn;
delimiter $$
create procedure create_sn()
begin
declare order_sn bigint(15);
declare prev bigint(15);
declare prevdatetime bigint(15);
declare sn bigint(15);
declare nowdate bigint(15);
select order_sn into prev from userorder order by userorder_id desc limit 1 ;
#赋值prev
SELECT prev;
select concat(year(prev),month(prev),dayofmonth(prev)) into prevdatetime;
select right(prev,4) into sn;
select concat(year(now()),month(now()),dayofmonth(now())) into nowdate;
if isnull(prev) then
select concat(nowdate,'0001') into order_sn;
select order_sn as ordersn;
elseif nowdate != prevdatetime then
select concat(nowdate,'00011') into order_sn;
select order_sn as ordersn;
else
select concat(prevdatetime,(sn+1)) into order_sn;
select order_sn as ordersn;
end if;
end
$$
delimiter ;
select * from userorder;
call create_sn();
随机字符串
drop procedure if exists randstr;
delimiter $$
create procedure randstr(num int(11))
#为了容易区分那个函数或者存储过程是那个数据库的,
#可以在函数名中加上数据库前缀test_randstr
begin
declare str char(255) default 'q1we23r4t5y6u7i8o9p0asdfghjklzxcvbnm';
declare nums int(11);
declare returnstr char(255);#SQL变量名不能和列名一样
declare i int(11) default 0;
select floor(truncate(rand(),1)*36)+1 into nums;
select substring(str,nums,1) into returnstr;
while i <num do
select floor(truncate(rand(),1)*36)+1 into nums;
select concat(substring(str,nums,1),returnstr) into returnstr;
#set returnstr=concat(substring(str,nums,1),returnstr);
set i=i+1;
end while;
select returnstr as randstr;
end
$$
delimiter ;
call randstr(5);
查看函数和存储过程的详情
show function status like '%rand%';
#查看函数的状态 包括:函数所属数据库,函数名,类型,创建时间,创建者,
#安全类型 注释 ,数据库字符集,客户端字符集
show procedure status like '%procedure_name'; #同上,这是存储过程
总结
- set关键字 声明一个全局变量
- declare 关键字声明一个局部变量
- 函数内部不能同时有局部变量和全局变量的存在
- end if 一般条件控制语句都会分开写
- 建议为存储过程添函数添加数据库前缀
- 存储过程可以返回表的数据,函数不可以
- SQL变量名不能和列名一样
- 在声明变量时一定要在begin语句之后,除begin外的任何语句之前