MySQL的存储过程

一、创建存储过程

存储过程就是具有名字的一段代码,用来完成一个特定的功能,

实质就是数据库 SQL 语言层面的代码封装与重用。

优点(为什么要用存储过程?):

  ①将重复性很高的一些操作,封装到一个存储过程中,简化了对这些SQL的调用

  ②批量处理:SQL+循环,减少流量,也就是“跑批”

  ③统一接口,确保数据的安全

CREATE
    [DEFINER = { user | CURRENT_USER }] 
 PROCEDURE sp_name ([proc_parameter[,...]])
    [characteristic ...] routine_body

proc_parameter: 
    [ IN | OUT | INOUT ] param_name type  --参数名 参数类型( IN | OUT | INOUT )
    
    --IN 	输入参数:表示调用者向过程传入值(传入值可以是字面量或变量)
	--OUT 	输出参数:表示过程向调用者传出值(可以返回多个值)(传出值只能是变量)
	--INOUT	输入输出参数:既表示调用者向过程传入值,又表示过程向调用者传出值(值只能是变量)
	-- 注:INOUT 尽量少用

characteristic: 
    COMMENT 'string'
  | LANGUAGE SQL
  | [NOT] DETERMINISTIC
  | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
  | SQL SECURITY { DEFINER | INVOKER }

routine_body:
  Valid SQL routine statement

[begin_label:] BEGIN
  [statement_list]
    ……
END [end_label]

(一)存储过程中的关键语法

声明语句的结束符,可以自定义

DELIMITER $$ --将语句的结束符号从分号;临时改为两个$$(可以是自定义)
或
DELIMITER //

声明存储过程

CREATE PROCEDURE demo_in_parameter(IN p_in int) --带参的存储过程(也可以不带参)

存储过程的开始和结束符号

BEGIN .... END    

定义变量

DECLAREvariable_name [,variable_name...] datatype [DEFAULT value];

其中,datatype 为 MySQL 的数据类型,如: int, float, date,varchar(length),例如:

DECLARE l_int int unsigned default 4000000; --int类型、无符号类型、默认4000000
DECLARE l_int int unsigned default 4000000;  
DECLARE l_numeric number(8,2) DEFAULT 9.95;  
DECLARE l_date date DEFAULT '1999-12-31';  
DECLARE l_datetime datetime DEFAULT '1999-12-31 23:59:59';  
DECLARE l_varchar varchar(255) DEFAULT 'This will not be padded';

变量赋值

SET @p_in=1  

调用存储过程

call sp_name[(传参)];  

(二)存储过程的控制语句

条件语句

(1)if-then-else语句:

if var=0 then --var代表一个变量
	--执行体,比如:
	insert into tableName values(17); --tableName里只有一个整型字段filedName
end if;  
if parameter=0 then 
	--执行体,比如:
  	update tableName set filedName=filedName+1;  
else 
	--执行体,比如:
  	update tableName set filedName=filedName+2;  
end if;  

(2)case语句:

case
    when var=0 then --var代表一个变量
        insert into tableName values(30);
    when var>0 then
    	insert into tableName values(20);
    when var<0 then
    	insert into tableName values(10);
    else
    	insert into tableName values(0);
end case
循环语句

(1)while ···· end while

类似C、C++、Java等语言中的while语句

while 条件 do
    -- 循环体
end while

示例:

declare var int;  
set var=0;  
while var<6 do  
	insert into tableName values(var);  
set var=var+1;  
end while;  

(2)repeat···· end repea

它在执行操作后检查结果,而 while 则是执行前进行检查。类似C、C++、Java等语言中的do ... while语句

repeat
    -- 循环体
until 循环条件  
end repeat;

示例:

declare v int; 
set v=0;  
repeat  
	insert into tableName values(v);  
set v=v+1;  
until v>=5
end repeat;

(3) loop ·····endloop

loop 循环和 repeat 循环相似, leave 语句的意义是离开循环。

declare v int;  
set v=0;  
LOOP_LABLE: loop
	insert into tableName values(v);
set v=v+1;
if v >=5 then 
	leave LOOP_LABLE; 
end if;  
end loop;

(4)ITERATE迭代

ITERATE 通过引用复合语句的标号,来从新开始复合语句:

declare v int;  
set v=0;  
LOOP_LABLE: loop  
	if v=3 then   
    	set v=v+1;  
    	ITERATE LOOP_LABLE;
    end if;  
    	insert into t values(v);  
    	set v=v+1;  
    if v>=5 then 
    	leave LOOP_LABLE;  
    end if;
end loop;  

(三)存储过程的示例

delimiter $$  --将语句的结束符号从分号;临时改为两个$$(可以是自定义)
DROP PROCEDURE IF EXISTS proc_batch_insert;
CREATE PROCEDURE proc_batch_insert()
BEGIN
DECLARE pre_name VARCHAR(255);--定义一个临时的字符类型的name
DECLARE ageVal INT;--定义一个临时的整型的age
DECLARE i INT;--定义一个整型的i
DECLARE pre_gender VARCHAR(255);--定义一个临时的字符类型的gender
DECLARE pre_nick_name VARCHAR(255);--定义一个临时的字符类型的nick_name

SET ageVal=100;--给ageVal变量赋值
SET i=1;--给i赋值初始值

WHILE i < 100 DO
		--循环产生一个随机的“姓名”的字符串,并赋值给pre_name
		SET pre_name=concat(substring('赵钱孙李龚',floor(1+5*rand()),1),substring('明国华建',floor(1+4*rand()),1),substring('明国华建',floor(1+4*rand()),1));
		--循环产生一个随机的“性别”的字符串,并赋值给pre_gender
		SET pre_gender=SUBSTRING('男女',floor(1+2*rand()),1);
		--循环产生一个随机的“昵称”的字符串,并赋值给pre_nick_name
		SET pre_nick_name=concat(substring('赵钱孙李',floor(1+4*rand()),1),substring('明国华建',floor(1+4*rand()),1),substring('明国华一',floor(1+4*rand()),1));
		INSERT INTO student(sname,age,gender,nick_name)VALUES(pre_name,(ageVal+i)%30,pre_gender,pre_nick_name);
SET i=i+1;
END WHILE;
END $$ --这里的$$相当于平时的SQL语句结束时的;   代表着语句真正结束

delimiter;  --将语句的结束符号恢复为分号
call proc_batch_insert(); --调用存储过程

解析:默认情况下,存储过程和默认数据库相关联,如果想指定存储过程创建在某个特定的数据库下,那么在过程名前面加数据库名做前缀。 在定义过程时,使用 DELIMITER $$ 命令将语句的结束符号从分号 ; 临时改为两个 $$,使得过程体中使用的分号被直接传递到服务器,而不会被客户端(如mysql)解释。

存储过程体

  • 存储过程体包含了在过程调用时必须执行的语句,例如:dml、ddl语句,if-then-else和while-do语句、声明变量的declare语句等

  • 过程体格式:以begin开始,以end结束(可嵌套)

    label1: BEGIN --“label1”为语句贴上标签
      label2: BEGIN
        label3: BEGIN
          statements; 
        END label3;
      END label2;
    END label1
    

注意: 每个嵌套块及其中的每条语句,必须以分号结束,表示过程体结束的begin-end块(又叫做复合语句compound statement),则不需要分号。

标签有两个作用:

  • 1、增强代码的可读性
  • 2、在某些语句(例如:leave和iterate语句),需要用到标签

二、修改存储过程

ALTER PROCEDURE 数据库.存储过程名;

三、删除存储过程

DROP PROCEDURE 数据库.存储过程名;

四、查询存储过程

SHOW CREATE PROCEDURE 数据库.存储过程名; -- 查看存储过程详细情况
select name from mysql.proc where db='数据库名'; -- 查询某个数据库中的存储过程的名字
select routine_name from information_schema.routines where routine_schema='数据库名';
-- 查询某个数据库中的routine_name
show procedure status where db='数据库名'; -- 查看某个存储过程的状态

MySQL中的触发器

!!尽量少使用触发器,不建议使用。

触发器是针对每一行的;对增删改非常频繁的表上切记不要使用触发器,因为它会非常消耗资源。

CREATE
    [DEFINER = { user | CURRENT_USER }]
TRIGGER trigger_name
trigger_time trigger_event
ON tbl_name FOR EACH ROW
  [trigger_order]
trigger_body

trigger_time: { BEFORE | AFTER } --触发时机:触发操作前、触发操作后

trigger_event: { INSERT | UPDATE | DELETE } --触发事件:增加、更新、删除

trigger_order: { FOLLOWS | PRECEDES } other_trigger_name

示例:通过触发器实现级联更新、删除

--级联更新的触发器
DROP TRIGGER IF EXISTS `ins_goods`;
create trigger ins_goods
after insert on goods for each row 
begin
	  insert into shop_goods ( cId, gId)
		values( new.cId, new.gId);
end;
--级联删除的触发器
DROP TRIGGER IF EXISTS `del_goods`;
create trigger del_goods
after delete on goods for each row 
begin
	  delete from shop_goods where cId = old.cId;
end;