首先创建如下两张表,并初始化一些数据。
基本语法格式如下:
CREATE PROCEDURE sp_name (parameters)
[characteristics ...] routine_body
其中:CREATE PROCEDURE为创建存储过程的关键字;sp_name为存储过程的名称(唯一性,没有所谓的重载方法概念);parameters为参数列表;characteristics指定存储过程的特性(该部分可以省略,即使用默认声明);routine_body是SQL代码的内容,可以用BEGIN...END来表示SQL代码的开始和结束。
parameters 表现形式 --> [IN(OUT/INOUT)] param_name type。IN代表入参、OUT代表出参、INOUT代表既可以是入参也可以是出参, param_name参数名称,type是类型(类型是mysql数据库中支持的任意类型,VARCHAR需要指定长度,如VARCHAR(255))。
characteristics 表现形式 -->
名称 | 说明 |
LANGUAGE SQL | 说明routine_body部分是由SQL语句组成的,当前系统支持的语言为SQL,SQL是LANGUAGE特性的唯一值 |
[NOT] DETERMINISTIC | 指明存储过程执行的结果是否确定。DETERMINISTIC表示结果是确定的。每次执行存储过程时,相同的输入会得到相同的输出。NOT DETERMINISTIC表示结果是不确定的,相同的输入可能得到不同的输出。如果没有指定任意一个值,默认为 NOT DETERMINISTIC。 |
{CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA} | 指明子程序使用SQL语句的限制。CONTAINS SQL表明子程序包含SQL语句,但是不包含读写数据的语句;NO SQL表明子程序不包含SQL语句;READS SQL DATA说明子程序包含读写读数据的SQL语句;MODIFIES SQL DATA表明子程序包含写数据的语句;默认情况下,系统会指定为CONTAINS SQL。 |
SQL SECURITY {DEFINER | INVOKER} | 指明谁有权限来执行。DEFINER表示只有定义者才能执行。INVOKER表示拥有权限的调用者可以执行。默认情况下,系统指定为DEFINER |
COMMENT 'string' | 注释信息,可以用来描述存储过程或存储函数。 |
--简单的示例:
DELIMITER $$ -- 声明结束符
CREATE PROCEDURE list_emp() -- 创建无参数存储过程
COMMENT '查询所有员工及其所在部门' -- characteristics 部分(可以省略)
BEGIN
SELECT * FROM tb_emp e
INNER JOIN tb_dept d
ON e.dept_id = d.dept_id;
END $$
DELIMITER ; -- 恢复默认结束符
CALL list_emp(); -- 调用存储过程
SHOW PROCEDURE STATUS LIKE '%list_emp%'; -- 查看存储过程
DROP PROCEDURE list_emp; -- 删除存储过程,目前编写错误的话直接删除重写
存储过程定义详情(SHOW PROCEDURE STATUS LIKE '%list_emp%'; -- 查看存储过程):
执行结果(CALL list_emp(); -- 调用存储过程):
--有参数的存储过程:
DELIMITER $$
CREATE PROCEDURE count_emp(OUT total_emp INT)
COMMENT '统计员工数量'
BEGIN
SELECT COUNT(*) INTO total_emp FROM tb_emp;
END $$
DELIMITER ;
SET @total_emp = 10; -- 声明变量
CALL count_emp(@total_emp); -- 调用存储过程
SELECT @total_emp; -- 获取结果
DROP PROCEDURE count_emp; -- 删除存储过程
执行结果:
总结说明:
1、这里的'DELIMITER $$'语句的作用是将mysql的结束符号设置为'$$',因为mysql默认的结束符号是';',为了避免与存储过程中的SQL语句结束符号冲突,需要使用'DELIMITER'改变存储过程的结束符,并以'END $$'结束存储过程。存储过程定义完毕再以'DELIMITER ;'恢复默认结束符号。亦可以指定其它符号为结束符,但是不能用反斜杠'\',它是mysql中的转义符。当然,简单的存储过程不更改结束符大多数情况下也是不会出现错误的。
2、有参数的时候,VARCHAR类型需要指定长度,比如VARCHAR(255)。
基本语法格式如下:
CREATE FUNCTION func_name(params)
RETURNS type
[characteristics ...] routine_body
其中:CREATE FUNCTION为创建存储函数的关键字;func_name为存储函数的名称(唯一性,没有所谓的重载方法概念);params为参数列表;RETURNS type语句表示函数返回数据的类型,可以是mysql中的任意数据类型;characteristics指定存储函数的特性(和存储过程一样);routine_body是存储函数主体。
参数列表:IN、OUT、或INOUT只对PROCEDURE是合法的(FUNCTION中总是默认IN参数,所以声明存储函数入参的时候不不能声明IN,会报错,因为默认是IN)。RETURNS子句只能对FUNCTION作指定,对函数而言这是强制的。它用来指定函数的返回类型,而且函数体必须包含一个RETURN value语句。
-- 简单的示例:
DELIMITER $$ -- 更改结束符
CREATE FUNCTION count_dept()
RETURNS INT
COMMENT '统计部门数量'
BEGIN
RETURN (SELECT COUNT(*) FROM tb_dept);
END $$
DELIMITER ; -- 还原结束符
DROP FUNCTION count_dept; -- 删除存储函数
SHOW FUNCTION STATUS LIKE '%count_dept%'; -- 查看存储函数定义
SELECT count_dept(); -- 调用存储函数
执行结果(SELECT count_dept(); -- 调用存储函数):
变量可以在子程序中声明并使用,这些变量的作用域范围是在BEGIN...END程序中,不能单独在存储过程外部声明变量。
1、定义变量
基本语法:
DECLARE var_name[,var_name1]... date_type [DEFAULT value];
var_name是局部变量的名称,可以同时声明多个变量,但是类型只能声明一次,就是说声明多个变量只能是同类型;date_type是变量类型,可以是mysql中任意的数据类型(VARCHAR需要指明长度,例如VARCHAR(255));DEFAULT 为变量设置默认值,如果是多参数,不能使用,默认值可以被声明为常量,也可以指定一个表达式。如果没有指定默认值,则为null。
简单的示例:
DECLARE param1 INT DEFAULT 10; -- 声明单参数
DECLARE param1, param2, param3 INT; -- 声明多参数
2、为变量赋值
定义变量后,可以为变量赋值以改变其值。
通过SET...为变量赋值,基本语法:
SET var_name = expr[,var_name=wxpr]....; -- 可以同时为单个或多个变量赋值,expr可以是具体的值,也可以是表达式。
--简单的示例
DECLARE param1, param2, param3 INT; -- 声明3个变量
SET param1=10, param2=20; -- 为param1和param2赋具体的值
SET param3 = param1 + param2; -- 将param1和param2的运算结果赋值给param3
通过SELECT...INTO...为一个或多个变量赋值,基本语法如下:
SELECT col_name[,...] INTO var_name[,...] table_expr;
这个SELECT语法把选定的列直接存储到对应位置的变量(所以要求col_name和var_name一一对应)。col_name表示字段名称,var_name表示定义的变量名称;table_expr表示查询条件表达式,包括名称和WHERE子句。
--简单的示例:
SELECT dept_name, dept_addr INTO v_dept_name, v_dept_addr FROM tb_dept WHERE dept_id = 1;
事务并不会影响存储过程或存储函数的执行顺序,也不会中断执行(存储过程或存储函数始终会执行到END)。
START TRANSACTION; -- 开始事务
COMMIT; -- 提交事务
ROLLBACK; -- 回滚事务
流程控制语句用来根据条件,控制语句的执行。mysql中用来构造控制流程的语句有:IF、CASE、LOOP、LEAVE、ITERATE、REPEAT、WHILE。
IF语句(说明:mysql中还有一个IF()函数,注意区别):
注意:IF语句必须配合THEN、END IF使用。除了IF还有ELSEIF(没有分开)和ELSE。
基本语法:
IF (expr_condition) THEN
....do something.....
ELSEIF (expr_condition) THEN
....do something.....
ELSE
....do something.....
END IF;
说明:如果表达式expr_condition计算结果为true,则执行THEN后面的逻辑,如果都不匹配则执行ELSE。条件判断表达式建议用括号包起来,增加可读性。
CASE语句
CASE语句也是一个条件判断语句。需要配合WHEN、THEN和END CASE使用。
CASE语句有两种语法格式:
CASE
WHEN expr_condition THEN ...do something...
WHEN expr_condition THEN ...do something...
....多个 WHEN THEN 语句.....
[ELSE ...do something...]
END CASE;
其中expr_condition为条件表达式,计算结果为true,则执行THEN后面的语句。多个WHEN...THEN依次执行,ELSE为可选条件。
另外一种语法格式:
CASE case_expr
WHEN expr THEN .....do something....
WHEN expr THEN .....do something...
....多个表达式...
[ELSE ...dosomething..]
END CASE;
其中,case_expr表示条件判断的表达式,WHEN后的表达式结果如果和case_expr匹配,则执行相应的THEN后面的语句。没有则执行ELSE,ELSE为可选。这种语法类似switch...case(建议使用第一种,逻辑清晰一点)。
LOOP、REPEAT、WHILE、LEAVE和ITERATE
其中LOOP、REPEAT和WHILE都是创建循环语句的关键词。
LEAVE用来退出任何被标注的循环语句(类似break,只能用于循环语句)。
ITERATE语句将执行顺序转到语句段开头处(类似continue,只能用于循环语句)。
LOOP基本语法(需要配合 END LOOP):
[loop_label]:LOOP
...do something....
END LOOP [loop_label];
其中,loop_label为可选,表示LOOP体的标签,do something为循环体。
LOOP简单示例(配合LEAVE和ITERATE):
DECLARE id INT DEFAULT 0;
add_loop:LOOP
SET id = id + 1;
IF(id>10) THEN
LEAVE add_loop;
ELSE
ITERATE add_loop;
END IF;
END LOOP add_loop;
REPEAT基本语法(需要配合 UNTIL和END REPEAT):
[repeat_label:] REPEAT
....do something....
UNTIL expr_condition;
END REPEAT [repeat_label];
其中,repeat_label为可选,表示REPEAT循环语句的标签,UNTIL指定循环条件。REPEAT执行过程是,每次循环体执行完毕需要去判断一下条件表达式expr_condition,如果为true继续执行,否则结束循环。
WHILE基本语法(需要配合DO和END WHILE使用):
[while_lebel:] WHILE expr_condition DO
.....do something....
END WHILE [while_label];
其中,while_label为可选,表示WHILE循环语句的标签。WHILE的执行过程是,先判断条件表达式expr_condition,如果为true执行循环,否则结束循环(与REPEAT的区别是:WHILE先判断条件,REPEAT是后判断条件)。
基本语法:
CALL sp_name([params,...]);
说明:如果存储过程没有参数,不能省略括号;参数个数要与存储过程定义的入参和出参个数匹配,用逗号隔开;出参类型的参数需要加上@符号。
基本语法:
SELECT func_name([params...]);
说明:如果存储函数没有参数,不能省略括号;因为存储函数都是入参,所以只需注意匹配个数即可。
基本语法:
SHOW [PROCEDURE | FUNCTION] STATUS [LIKE 'pattern'];
说明:查看存储过程即PROCEDURE、存储函数即FUNCTION。LIKE为过滤条件(按存储过程、函数名字匹配),如果没有则是查看所有定义。
SHOW [CREATE | PROCEDURE] FUNCTION sp_name;
此语句查看定义存储函数、存储过程的脚本。
基本语法:
DROP [PROCEDURE | FUNCTION] [IF EXISTS] sp_name;
说明:sp_name为存储过程或存储函数的全称。[IF EXISTS]是mysql的一个扩展,建议使用。如果存储过程或函数不存在,它可以防止错误的产生,但是会产生一个用SHOW WARNINGS查看的警告。
注意:只能修改存储过程或函数的定义,不能修改执行的逻辑代码或参数。
基本语法:
ALTER [PROCEDURE | FUNCTION] sp_name [characteristic.....];
其中,sp_name为存储过程或函数的名称,characteristic指定存储过程的特性。可以修改的特性和定义存储过程的时候的可选特性是一样的,不重复累赘。
1、存储过程和存储函数的区别(除了关键字:PROCEDURE、FUNCTION)
本质上都是存储程序。
参数类型不同:存储函数不允许声明出参类型,只能通过return关键字返回;
调用方式不同:存储过程用CALL,存储函数用SELECT;
存储函数限制比较多,多以建议使用存储过程,慎用存储函数。
2、修改存储过程、存储函数
存储过程或存储函数中的代码是不提供修改的,只能通过drop删除后,重新编写,或者直接编写一个新的程序。只能修改存储过程或存储函数的特性。
3、存储过程和存储函数可以相互调用
存储过程和存储函数包含自定义的SQL语句集合,所以,可以使用CALL或SELECT调用其它存储过程和存储函数。但是不能使用DROP删除其它存储过程和存储函数。
4、注意区别参数名字和表中的字段名
在定义存储过程和存储函数的时候,参数名称一定要与表中的字段名区别开来,否则可能出现无法预期的结果。