RDBMS关系数据库系统之​ 存储过程

举报
tea_year 发表于 2024/01/16 22:25:21 2024/01/16
【摘要】 本章学习目标​ 理解存储过程​ 熟练掌握存储过程的相关操作在实际开发中,经常会遇到为了完成某一功能,需要编写一组SQL语句,为了提高SQL语句的重用性,MySQL提供了存储过程,本章将对MySQL存储过程详细讲解。9.1​ 存储过程概述9.1.1​ 什么是存储过程存储过程是将SQL语句放入一个集合里,然后直接调用存储过程来执行已经定义好的SQL语句集合,这样做可以避免开发人员重复编写相同...

本章学习目标

​ 理解存储过程

​ 熟练掌握存储过程的相关操作

在实际开发中,经常会遇到为了完成某一功能,需要编写一组SQL语句,为了提高SQL语句的重用性,MySQL提供了存储过程,本章将对MySQL存储过程详细讲解。

9.1​ 存储过程概述

9.1.1​ 什么是存储过程

存储过程是将SQL语句放入一个集合里,然后直接调用存储过程来执行已经定义好的SQL语句集合,这样做可以避免开发人员重复编写相同的SQL语句。另外,存储过程还可以减少数据在数据库和应用服务器之间的传输,可以提高数据处理的效率。

9.1.2​ 存储过程的优缺点

存储过程的优点如下所示。

​ 允许标准组件式编程,提高了SQL语句的重用性、共享性和可移植性。

​ 能够实现较快的执行速度,节省网络流量。

​ 可以作为一种安全机制来使用。

存储过程的缺点如下所示。

​ 编写存储过程比单句SQL语句复杂,需要用户具有丰富的经验。

​ 在编写存储过程时,需要创建这些数据库对象的权限。

9.2​ 存储过程的相关操作

上节内容详细阐述了存储过程的基本概念,接下来将讲解存储过程的相关操作,包含创建、修改、删除和查看存储过程。

9.2.1​ 数据准备

在讲解存储过程前,首先创建三张数据表并插入数据,用于后面的例题演示,分别为用户表users、学生表stu和学生分数表stu_score,首先来了解一下表结构,用户表users的表结构如表9.1所示。

表9.1​ users表

字段

字段类型

说明

id

int

用户编号

name

varchar(50)

用户姓名

age

int

用户年龄

email

varchar(50)

用户邮箱

表9.1中列出了users表的字段、字段类型和说明,接着创建users表,SQL语句如下所示。

mysql> CREATE TABLE users(
-> ID INT PRIMARY KEY,
-> NAME VARCHAR(50),
-> AGE INT,
-> EMAIL VARCHAR(50)
-> );

Query OK, 0 rows affected (0.21 sec)

users表创建完成后,向表中插入数据,SQL语句如下所示。

mysql> INSERT INTO users(ID,NAME,AGE,EMAIL)
-> VALUES
-> (1,'zs',22,'zs@qq.com'),
-> (2,'ls',25,'ls@qq.com'),
-> (3,'ww',28,'ww@qq.com');

Query OK, 3 rows affected (0.05 sec)

Records: 3 Duplicates: 0 Warnings: 0

以上执行结果证明数据插入完成,可以查看表中数据,SQL语句如下所示。

mysql> SELECT * FROM users;
+----+------+------+-----------+
| ID | NAME | AGE | EMAIL |
+----+------+------+-----------+
| 1 | zs | 22 | zs@qq.com |
| 2 | ls | 25 | ls@qq.com |
| 3 | ww | 28 | ww@qq.com |
+----+------+------+-----------+
3 rows in set (0.00 sec)

接着创建学生表stu,表结构如表9.2所示。

表9.2​ stu表

字段

字段类型

说明

stu_id

int

学生编号

stu_name

char(10)

学生姓名

stu_class

int

学生班级

stu_sex

char(2)

学生性别

stu_age

int

学生年龄

表9.2中列出了stu表的字段、字段类型和说明,接着创建stu表,SQL语句如下所示。

mysql> CREATE TABLE stu(
-> STU_ID INT NOT NULL,
-> STU_NAME CHAR(10) NOT NULL,
-> STU_CLASS INT NOT NULL,
-> STU_SEX CHAR(2) NOT NULL,
-> STU_AGE INT NOT NULL,
-> PRIMARY KEY (STU_ID)
-> );

Query OK, 0 rows affected (0.09 sec)

stu表创建完成后,向表中插入数据,SQL语句如下所示。

mysql> INSERT INTO stu VALUES
-> (1,'aa',3,'女',23),
-> (2,'bb',1,'男',12),
-> (3,'cc',30,'女',11),
-> (4,'dd',2,'男',22),
-> (5,'ee',1,'女',23),
-> (6,'ff',2,'女',13),
-> (7,'gg',3,'男',10),
-> (8,'hh',2,'女',11),
-> (9,'ii',1,'男',13),
-> (10,'jj',3,'女',27);
Query OK, 10 rows affected (0.06 sec)

Records: 10 Duplicates: 0 Warnings: 0

最后创建学生成绩表stu_score,表结构如表9.3所示。

表9.3​ stu_score表

字段

字段类型

说明

stu_id

int

学生编号

stu_score

int

学生分数

表9.3中列出了stu_score表的字段、字段类型和说明,接着创建stu_score表,SQL语句如下所示。

mysql> CREATE TABLE stu_score(
-> stu_id INT NOT NULL,
-> stu_score INT NOT NULL,
-> FOREIGN KEY(stu_id) REFERENCES stu(stu_id)
-> );

Query OK, 0 rows affected (0.16 sec)

stu_score表创建完成后,向表中插入数据,SQL语句如下所示。

mysql> INSERT INTO stu_score VALUES
-> (1,91),(2,62),(3,18),
-> (4,95),(5,71),(6,82),
-> (7,60),(8,52),(9,99),
-> (10,46);
Query OK, 10 rows affected (0.06 sec)

Records: 10 Duplicates: 0 Warnings: 0

至此,三张数据表创建完成,本章后面的演示例题会用到这三张表。

9.2.2​ 创建存储过程

在创建存储过程时,当前用户必须具有创建存储过程的权限。此时登录的是root用户,查询该用户是否具有创建存储过程的权限,SQL语句如下所示。

mysql> select Create_routine_priv from mysql.user WHERE User='root';
+---------------------+
| Create_routine_priv |
+---------------------+
| Y |
| Y |
+---------------------+

2 rows in set (0.00 sec)

以上执行结果可看出,当前用户具有创建存储过程的权限,可以进行创建存储过程的操作,MySQL中创建存储过程的语法格式如下所示。

CREATE PROCEDURE sp_name([proc_parameter[...]])

[characteristic ...] routine_body

以上语法格式中,创建存储过程的语句由多条子句构成,为了帮助大家更好地理解,接下来对语法格式中的每个部分进行详细解析,具体如下所示。

​ CREATE PROCEDURE:表示创建存储过程的关键字。

​ sp_name:表示存储过程的名称。

​ proc_parameter:表示存储过程的参数列表。

​ characteristic:用于指定存储过程的特性。

​ routine_body:表示存储过程的主体部分,包含了在过程调用的时候必须执行的sql语句。以BEGIN开始,以END结束。如果存储过程体中只有一条sql语句,可以省略BEGIN-END标志。

以上是创建存储过程的语法格式,proc_parameter为指定存储过程的参数列表,该参数列表的形式如下所示。

[IN|OUT|INOUT] param_name type

以上参数列表中,IN表示输入参数,OUT表示输出参数,INOUT表示既可以输入也可以输出;param_name表示参数名称;type表示参数的类型,可以是MySQL中的任意类型。

另外,创建存储过程的格式中,characteristic有五种可选值,具体如下所示。

​ COMMENT 'string':用于对存储过程的描述,其中string为描述内容,comment为关键字。

​ LANGUAGE SQL:用于指明编写存储过程的语言为SQL语言。

​ DETERMINISTIC:表示存储过程对同样的输入参数产生相同的结果;NOT DETERMINISTIC则表示会产生不确定的结果(默认)。

​ contains sql | no sql | reads sql data | modifies sql data:表示存储过程包含读或写数据的语句,no sql表示不包含sql语句,reads sql data表示存储过程只包含读数据的语句,modifies sql data 表示存储过程只包含写数据的语句。

​ sql security definer | invoker:指定有权限执行存储过程的用户,其中definer代表定义者,invoker代表调用者,默认是definer。

接下来通过具体案例演示如何创建存储过程。

例9-1​ 创建一个带IN的存储过程,用于通过传入用户名,查询表users中的用户信息,SQL语句如下所示。

mysql> delimiter //

mysql> CREATE PROCEDURE SP_SEARCH(IN p_name CHAR(20))
-> BEGIN
-> IF p_name is null or p_name='' THEN
-> SELECT * FROM users;
-> ELSE
-> SELECT * FROM users WHERE name LIKE p_name;
-> END IF;
-> END//

Query OK, 0 rows affected (0.06 sec)

以上SQL语句中,“delimiter //”语句的作用是将MySQL的结束符设置为“//”,因为MySQL默认的语句结束符号为分号,与存储过程中的语句结束符冲突,所以需要“delimiter //”语句改变默认的结束符,最后以“END//”语句结束存储过程。存储过程创建完成后,可以使用“delimiter”语句恢复默认结束符,具体如下所示。

mysql> delimiter ;

以上SQL恢复了默认结束符,需要注意的是,“delimiter”与设定的结束符之间一定要有一个空格,否则设定无效。存储过程创建完成后,可以使用CALL关键字调用存储过程,SQL语句如下所示。

mysql> CALL SP_SEARCH('zs');

+----+------+------+-----------+

| ID | NAME | AGE | EMAIL |

+----+------+------+-----------+

| 1 | zs | 22 | zs@qq.com |

+----+------+------+-----------+

1 row in set (0.00 sec)

Query OK, 0 rows affected (0.02 sec)

从以上执行结果可看出,通过CALL关键字调用了存储过程SP_SEARCH,传入了参数“zs”,执行存储过程后,成功查询到了用户“zs”的信息。

例9-2​ 创建一个带OUT的存储过程,用于通过传入用户年龄,查询表users中大于该年龄的用户信息,并且输出查询到的用户个数,SQL语句如下所示。

mysql> delimiter //

mysql> CREATE PROCEDURE SP_SEARCH2(IN p_age INT,OUT p_int INT)

-> BEGIN

-> IF p_age is null or p_age='' THEN

-> SELECT * FROM users;

-> ELSE

-> SELECT * FROM users WHERE age>p_age;

-> END IF;

-> SELECT FOUND_ROWS() INTO p_int;

-> END//

Query OK, 0 rows affected (0.03 sec)

以上执行结果证明存储过程创建成功,接着调用存储过程,SQL语句如下所示。

mysql> CALL SP_SEARCH2(22,@p_num);

+----+------+------+-----------+

| ID | NAME | AGE | EMAIL |

+----+------+------+-----------+

| 2 | ls | 25 | ls@qq.com |

| 3 | ww | 28 | ww@qq.com |

+----+------+------+-----------+

2 rows in set (0.00 sec)

Query OK, 1 row affected (0.01 sec)

从以上执行结果可以看出,通过CALL关键字调用了存储过程SP_SEARCH2,传入了参数“22”,执行存储过程后,成功查询到了users表中年龄大于22的用户信息,@p_num可以查询到存储过程执行后的输出内容,即用户的个数,SQL语句如下所示。

mysql> SELECT @p_num;
+--------+
| @p_num |
+--------+
| 2 |

+--------+

1 row in set (0.00 sec)

从以上执行结果可看出,通过查询@p_num,查看到了存储过程执行后的输出内容,年龄大于22的用户有两个。

例9-3​ 创建一个带INOUT的存储过程,用于将输入的参数乘以10并输出,SQL语句如下所示。

mysql> delimiter //

mysql> CREATE PROCEDURE SP_INOUT(INOUT p_num INT)

-> BEGIN

-> SET p_num=p_num*10;

-> END//

Query OK, 0 rows affected (0.00 sec)

以上执行结果证明存储过程创建成功,接着调用存储过程,SQL语句如下所示。

mysql> delimiter ;

mysql> SET @p_num2=5;

Query OK, 0 rows affected (0.00 sec)

mysql> CALL SP_INOUT(@p_num2);

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @p_num2;

+---------+

| @p_num2 |

+---------+

| 50 |

+---------+

1 row in set (0.00 sec)

以上SQL语句中,首先通过SET关键字定义@p_num2等于5,然后通过CALL关键字调用存储过程SP_INOUT,将参数@p_num2传入,最后通过SELECT关键字查询@p_num2的值,输出结果为50,存储过程的功能成功实现。

9.2.3​ 查看存储过程

查看存储过程有三种方式,接下来针对三种方式分别讲解。

1.使用SHOW STATUS语句查看存储过程

查询存储过程的状态,可以使用SHOW STATUS语句,具体语法格式如下。

SHOW {PROCEDURE | FUNCTION} STATUS [LIKE ‘pattern’]

接下来通过具体案例演示查看存储过程的状态。

例9-4​ 使用SHOW STATUS语句查看所有名称以“S”开头存储过程的状态,SQL语句如下所示。

mysql> SHOW PROCEDURE STATUS LIKE 'S%'\G

*************************** 1. row ***************************

Db: qianfeng6

Name: SP_INOUT

Type: PROCEDURE

Definer: root@localhost

Modified: 2017-11-30 15:34:12

Created: 2017-11-30 15:34:12

Security_type: DEFINER

Comment:

character_set_client: gbk

collation_connection: gbk_chinese_ci

Database Collation: utf8_general_ci

*************************** 2. row ***************************

Db: qianfeng6

Name: SP_SEARCH

Type: PROCEDURE

Definer: root@localhost

Modified: 2017-11-30 14:39:42

Created: 2017-11-30 14:39:42

Security_type: DEFINER

Comment:

character_set_client: gbk

collation_connection: gbk_chinese_ci

Database Collation: utf8_general_ci

*************************** 3. row ***************************

Db: qianfeng6

Name: SP_SEARCH2

Type: PROCEDURE

Definer: root@localhost

Modified: 2017-11-30 15:16:54

Created: 2017-11-30 15:16:54

Security_type: DEFINER

Comment:

character_set_client: gbk

collation_connection: gbk_chinese_ci

Database Collation: utf8_general_ci

3 rows in set (0.01 sec)

以上执行结果可看到数据库中一共有三个名称以“S”开头的存储过程,通过SHOW STATUS语句可以查看到三个存储过程的Db、Name、Type等。

2.使用SHOW CREATE语句查看存储过程

查询存储过程的创建信息,可以使用SHOW CREATE语句,具体语法格式如下。

SHOW CREATE{PROCEDURE | FUNCTION} sp_name

接下来通过具体案例演示查看存储过程的创建信息。

例9-5​ 使用SHOW CREATE语句查看存储过程SP_SEARCH2的创建信息,SQL语句如下所示。

mysql> SHOW CREATE PROCEDURE SP_SEARCH2\G

*************************** 1. row ***************************

Procedure: SP_SEARCH2

sql_mode:
Create Procedure: CREATE DEFINER=`root`@`localhost` PROCEDURE

`SP_SEARCH2`(IN p_age INT,OUT p_int INT)

BEGIN

          IF p_age is null or p_age='' THEN

         SELECT * FROM users;

ELSE

      SELECT * FROM users WHERE age>p_age;

     END IF;

SELECT FOUND_ROWS() INTO p_int;

END

character_set_client: gbk

collation_connection: gbk_chinese_ci

Database Collation: utf8_general_ci

1 row in set (0.00 sec)

以上执行结果可看到,通过SHOW CREATE语句可以查看存储过程SP_SEARCH2的Procedure、Create Procedure等。

3.从information_schema.Routines表中查看存储过程

在MySQL中,存储过程的信息存储在information_schema库下的Routines表中,可以通过查询该表的数据来查询存储过程的信息,接下来通过具体案例演示查看Routines表中存储过程的信息。

例9-6​ 通过查询information_schema.Routines表,查看存储过程SP_SEARCH的信息,SQL语句如下所示。

mysql> SELECT * FROM information_schema.ROUTINES

-> WHERE ROUTINE_NAME='SP_SEARCH' AND ROUTINE_TYPE='PROCEDURE'\G

*************************** 1. row ***************************

SPECIFIC_NAME: SP_SEARCH

ROUTINE_CATALOG: def

ROUTINE_SCHEMA: qianfeng6

ROUTINE_NAME: SP_SEARCH

ROUTINE_TYPE: PROCEDURE

DATA_TYPE:

CHARACTER_MAXIMUM_LENGTH: NULL

CHARACTER_OCTET_LENGTH: NULL

NUMERIC_PRECISION: NULL

NUMERIC_SCALE: NULL

CHARACTER_SET_NAME: NULL

COLLATION_NAME: NULL

DTD_IDENTIFIER: NULL

ROUTINE_BODY: SQL

ROUTINE_DEFINITION: BEGIN

IF p_name is null or p_name='' THEN

SELECT * FROM users;

ELSE

SELECT * FROM users WHERE name LIKE p_name;

END IF;

END

EXTERNAL_NAME: NULL

EXTERNAL_LANGUAGE: NULL

PARAMETER_STYLE: SQL

IS_DETERMINISTIC: NO

SQL_DATA_ACCESS: CONTAINS SQL

SQL_PATH: NULL

SECURITY_TYPE: DEFINER

CREATED: 2017-11-30 14:39:42

LAST_ALTERED: 2017-11-30 14:39:42

SQL_MODE:

ROUTINE_COMMENT:

DEFINER: root@localhost

CHARACTER_SET_CLIENT: gbk

COLLATION_CONNECTION: gbk_chinese_ci

DATABASE_COLLATION: utf8_general_ci

1 row in set (0.01 sec)

以上执行结果可看到,通过查询information_schema.Routines表,不仅可以查看到存储过程SP_SEARCHE的基本信息,还可以查看存储过程SP_SEARCHE的创建语句。

9.2.4​ 修改存储过程

在修改存储过程时,当前用户必须具有修改存储过程的权限。此时登录的是root用户,查询该用户是否具有修改存储过程的权限,SQL语句如下所示。

mysql> select Alter_routine_priv from mysql.user WHERE User='root';

+--------------------+

| Alter_routine_priv |

+--------------------+

| Y |

| Y |

+--------------------+

2 rows in set (0.00 sec)

以上执行结果可看出,当前用户具有修改存储过程的权限,可以进行修改存储过程的操作,MySQL中修改存储过程的语法格式如下所示。

ALTER {PROCEDURE | FUNCTION} sp_name [characteristic ...]

以上语法格式中,sp_name表示存储过程的名称,characteristic表示修改存储过程的具体部分,有六个可选值,具体如下所示。

​ CONTAINS SQL:表示子程序包含SQL语句,但不包含读或写数据的语句。

​ NO SQL:表示子程序中不包含SQL语句。

​ READS SQL DATA:表示程序中包含读数据的语句。

​ MODIFIES SQL DATA:表示子程序中包含写数据的语句。

​ SQL SECURITY{DEFINER | INVOKER}:指明有权限执行的用户,其中DEFINER表示只有定义者才能执行,INVOKER表示只有调用者才可以执行。

​ COMMENT ‘string’:表示注释信息。

接下来通过具体案例演示如何修改存储过程。

例9-7​ 修改存储过程SP_SEARCH的定义,将读写权限修改为MODIFIES SQL DATA,并指明只有调用者可执行,SQL语句如下所示。

mysql> ALTER PROCEDURE SP_SEARCH

-> MODIFIES SQL DATA

-> SQL SECURITY INVOKER;

Query OK, 0 rows affected (0.01 sec)

以上执行结果证明存储过程修改完成,可以通过查询information_schema.Routines表,查看存储过程SP_SEARCH是否修改,SQL语句如下所示。

mysql> SELECT SPECIFIC_NAME,SQL_DATA_ACCESS,SECURITY_TYPE
-> FROM information_schema.Routines
-> WHERE ROUTINE_NAME='SP_SEARCH' AND ROUTINE_TYPE='PROCEDURE';
+---------------+-------------------+---------------+
| SPECIFIC_NAME | SQL_DATA_ACCESS | SECURITY_TYPE |
+---------------+-------------------+---------------+
| SP_SEARCH | MODIFIES SQL DATA | INVOKER |
+---------------+-------------------+---------------+
1 row in set (0.01 sec)

从以上执行结果可看出,存储过程SP_SEARCH的定义成功修改,目前MySQL还不支持对已经存在的存储过程代码进行修改,如果要修改存储过程的代码,只能重新创建一个存储过程。

9.2.5​ 删除存储过程

在删除存储过程时,当前用户必须具有修改存储过程的权限。此时登录的是root用户,具体修改存储过程的权限,这里就不再验证,删除存储过程的语法格式如下所示。

DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name

以上语法格式中,sp_name是要删除的存储过程名称,IF EXISTS是可选的,表示如果存储过程不存在,不发生错误,而是产生一个警告,接下来通过具体案例演示删除存储过程。

例9-8​ 存储过程SP_SEARCH删除,SQL语句如下所示。

mysql> DROP PROCEDURE SP_SEARCH;

Query OK, 0 rows affected (0.02 sec)

以上执行结果证明存储过程删除成功。

9.2.6​ 变量的使用

存储过程中可以使用变量,在MySQL5.1以后,变量是不区分大小写的,变量可以在子程序中声明并使用,这些变量的作用范围是在BEGIN-END程序中。在存储过程中使用变量,首先要定义变量,MySQL提供了DECLARE语句定义变量,具体语法格式如下所示。

DECLARE var_name[,varname]…date_typey[DEFAULT value];

以上语法格式中,var_name为局部变量的名称,可以定义多个变量,用逗号分隔开,DEFAULT value子句可以为变量提供默认值,如果没有该语句,变量的初始值为NULL,接下来定义一个名称为tmp的变量,类型为varchar(10),默认值为abc,具体代码如下所示。

DECLARE tmp varchar(10) DEFAULT ‘abc’;

以上代码中定义了变量tmp,MySQL提供了SET语句为变量赋值,具体语法格式如下所示。

SET var_name=expr[,var_name=expr]…;

如上语法格式中,var_name代表变量名,expr代表为变量赋的值,可以为多个变量赋值,用逗号分隔开即可,接下来声明变量tmp,然后使用SET语句为变量赋值为10,具体代码如下所示。

DECLARE tmp INT;

SET tmp=10;

以上代码中,使用DECLARE语句声明INT类型变量tmp,然后使用SET语句将变量tmp赋值为10,以上为变量声明和赋值的实现。需要注意的是,局部变量只能在存储过程体的BEGIN-END语句块中声明。

9.2.7​ 定义条件和处理程序

在开发中,可能会对特定的条件进行处理,定义条件是事先定义程序执行过程中遇到的问题,处理程序是定义了在遇到问题时,应采取什么样的处理方式,接下来对定义条件和处理进行详细讲解。

1.定义条件

MySQL中使用DECLARE语句可以定义条件,具体语法格式如下所示。

DECLARE condition_name CONDITION FOR[condition_type];

[condition_type]:

SQLSTATE[VALUE] sqlstate_value | mysql_error_code

以上语法格式中,condition_name代表定义的条件名称,condition_type代表条件的类型,sqlstate_value和mysql_error_code都可以表示MySQL的错误,其中sqlstate_value是长度为5的字符串类型错误代码,mysql_error_code是数值类型的错误代码,接下来通过具体案例演示如何定义条件。

例9-9​ 定义“ERROR1148(42000)”错误,名称为command_not_allowed,具体代码如下所示。

DECLARE command_not_allowed CONDITION FOR SQLSTATE ‘42000’;

或者

DECLARE command_not_allowed CONDITION FOR 1148;

以上代码两种写法都可以实现定义“ERROR1148(42000)”错误,因为“ERROR1148(42000)”中,sqlstate_value的值是长度为5的字符串’42000’,mysql_error_code的值是数值类型1148。

2.定义处理程序

定义了条件后,还需要定义对应的处理程序,MySQL中使用DECLARE语句定义处理程序,具体语法格式如下所示。

DECLARE handler_type HANDLER FOR condition_value[,…] sp_statement

handler_type:

CONTINUE | EXIT | UNDO

condition_value:

|condition_name

|SQLWARNING

|NOT FOUND

|SQLEXCEPTION

|mysql_error_code

以上语法格式中,handler_type为错误处理方式,参数取值有三个,其中CONTINUE表示遇到错误不处理,继续执行;EXIT表示遇到错误马上退出;UNDO表示遇到错误后,撤销之前的操作,MySQL中暂时不支持这样的操作。Sp_statement为程序语句段,表示在遇到定义的错误时,需要执行的存储过程。condition_value表示错误类型,有六个可选值,具体如下所示。

​ SQLSTATE[VALUE] sqlstate_value:包含5个字符的字符串错误值。

​ condition_name:表示DECLARE CONDITION定义的错误条件名称。

​ SQLWARNING:匹配所有以01开头的SQLSTATE错误代码。

​ NOT FOUND:匹配所有以02开头的SQLSTATE错误代码。

​ SQLEXCEPTION:匹配所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE错误代码。

​ mysql_error_code:匹配数值类型错误代码。

另外,定义处理程序有六种方式,具体如下所示。

// 方法一,捕获sqlstate_value

DECLARE CONTINUE HANDLER FOR SQLSTATE ‘42S02’

SET @info=’NO_SUCH_TABLE’;

// 方法二,捕获mysql_error_code

DECLARE CONTINUE HANDLER FOR 1146 SET @info=’NO_SUCH_TABLE’;

// 方法三,先定义条件,然后调用

DECLARE no_such_table CONDITION FOR 1146;

DECLARE CONTINUE HANDLER FOR NO_SUCH_TABLE SET @info=’ERROR’;

// 方法四,使用SQLWARNING

DECLARE EXIT HANDLER FOR SQLWARNING SET @info=’ERROR’;

// 方法五,使用NOT FOUND

DECLARE EXIT HANDLER FOR NOT FOUND SET @info=’NO_SUCH_TABLE’;

// 方法六,使用SQLEXCEPTION

DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info=’ERROR’;

以上为六种处理程序的方式,接下来分别讲解这些处理方式,具体如下所示。

​ 第一种方法是捕获sqlstate_value值,如果遇到sqlstate_value值为’42S02’,则执行CONTINUE操作,并且输出”NO_SUCH_TABLE”信息。

​ 第二种方法是捕获mysql_error_code值,如果遇到mysql_error_code值为1146,则执行CONTINUE操作,并且输出”NO_SUCH_TABLE”信息。

​ 第三种方法是先定义条件,然后再调用条件,这里先定义no_such_table条件,遇到1146错误,就执行CONTINUE操作。

​ 第四种方法是使用SQLWARNING,SQLWARNING捕获所有以01开头的sqlstate_value值,然后执行EXIT操作,并且输出ERROR信息。

​ 第五种方法是使用NOT FOUND,NOT FOUND捕获所有以02开头的sqlstate_value值,然后执行EXIT操作,并且输出”NO_SUCH_TABLE”信息。

​ 第六种方法是使用SQLEXCEPTION,SQLEXCEPTION捕获所有没有被SQLWARNING或NOT FOUND捕获的sqlstate_value值,然后执行EXIT操作,并且输出”ERROR”信息。

9.2.8​ 光标的使用

查询语句可能返回多条记录,在存储过程中可以使用光标,用于逐条读取查询结果集中的记录。光标的使用包括声明光标、打开光标、使用光标和关闭光标。接下来详细讲解光标的相关内容。

1.声明光标

在使用光标前,需要先声明光标,并且必须声明在处理程序之前,变量和条件之后,MySQL中使用DECLARE关键字声明光标,具体语法格式如下所示。

DECLARE cursor_name cursor FOR select_statement;

以上语法格式中,cursor_name表示光标的名称,select_statement表示SELECT语句的内容,返回一个用于创建光标的结果集。接下来声明一个名为cur_employee的光标,具体代码如下所示。

Declare cur_employee cursor FOR select name,age from employee;

通过以上代码,成功声明了一个名为cur_employee的光标。

2.打开光标

声明光标后,若想使用光标,需要先打开光标,具体语法格式如下所示。

Open cursor_name

以上语法格式中,cursor_name表示光标的名称,Open是打开光标的关键字,接下来将光标cur_employee打开,具体代码如下所示。

Open cur_employee;

通过以上代码,成功打开了名为cur_employee的光标。

3.使用光标

在MySQL中,使用Fetch关键字来使用光标,具体语法格式如下所示。

Fetch cur_name INTO var_name[,var_name…];

以上语法格式中,cursor_name表示光标的名称,var_name表示将光标中的select语句查询出来的信息存入该参数,var_name必须在声明光标前定义。接下来使用名称为cur_employee的光标,将查询出来的信息存入emp_name和emp_age,具体代码如下所示。

Fetch cur_employee INTO emp_name,emp_age;

通过以上代码,成功使用了名为cur_employee的光标,并且将查询出来的信息存入emp_name和emp_age。

4.关闭光标

使用完光标后,需要关闭光标,具体语法格式如下所示。

Close cursor_name;

以上语法格式中,cursor_name表示光标的名称,Close是关闭光标的关键字,接下来将光标cur_employee关闭,具体代码如下所示。

Close cur_employee;

通过以上代码,成功关闭了名为cur_employee的光标。

9.2.9​ 流程控制

在编写存储过程时,还可以自定义流程控制,实现多个SQL语句划分或组合成符合业务逻辑的代码块,MySQL中流程控制语句包含IF语句、CASE语句、WHILE语句等,接下来详细讲解存储过程中的流程控制语句。

1.IF语句

IF语句是指若满足某种条件,就根据判断的结果为TRUE或FALSE执行相应的语句,具体语法格式如下所示。

IF search_condition THEN statement_list

[ELSEIF search_condition THEN statement_list]

[ELSE statement_list]

END IF

以上语法格式中,search_condition表示条件判断语句,statement_list表示不同条件的执行语句,接下来通过具体案例演示IF语句的用法。

例9-10​ 使用IF语句编写存储过程,通过传入的参数等级,可以返回各个分数等级的学生编号和学生分数,SQL语句如下所示。

mysql> delimiter //

mysql> CREATE PROCEDURE SP_SCHOLARSHIP_LEVEL(IN p_level char(1))

-> BEGIN

-> IF p_level ='A' THEN

-> SELECT * FROM stu_score WHERE STU_SCORE >=90;

-> ELSEIF p_level ='B' THEN

-> SELECT * FROM stu_score WHERE STU_SCORE <90 AND STU_SCORE>=80;

-> ELSEIF p_level ='C' THEN

-> SELECT * FROM stu_score WHERE STU_SCORE <80 AND STU_SCORE>=70;

-> ELSEIF p_level ='D' THEN

-> SELECT * FROM stu_score WHERE STU_SCORE <60;

-> ELSE

-> SELECT * FROM stu_score;

-> END IF;

-> END//

Query OK, 0 rows affected (0.00 sec)

以上执行结果证明存储过程创建完成,接下来调用存储过程,返回等级A的学生编号和学生分数,SQL语句如下所示。

mysql> delimiter ;

mysql> CALL SP_SCHOLARSHIP_LEVEL('A');

+--------+-----------+

| stu_id | stu_score |

+--------+-----------+

| 1 | 91 |

| 4 | 95 |

| 9 | 99 |

+--------+-----------+

3 rows in set (0.02 sec)

Query OK, 0 rows affected (0.03 sec)

以上执行执行结果可以看出,通过调用存储过程SP_SCHOLARSHIP_LEVEL,传入参数等级,成功查询到分数等级A的学生编号和学生分数。

2.CASE语句

CASE语句是另一种进行条件判断的语句,具体语法格式如下所示。

Case case_value

         When when_value then statement_list

         [When when_value then statement_list]…

        [else statement_list]

End case

以上语法格式中,case_value表示条件判断的表达式,决定了哪个WHEN子句会被执行,when_value表示表达式可能的值,如果某个when_value表达式与case_value表达式结果相同,则执行对应THEN关键字后的statement_list中的语句,statement_list表示不同when_value值的执行语句,接下来通过具体案例演示CASE语句的用法。

例9-11​ 使用CASE语句编写存储过程,通过传入的参数等级,可以返回各个分数等级的学生信息,SQL语句如下所示。

mysql> delimiter //

mysql> CREATE PROCEDURE SP_SCHOLARSHIP_LEVEL3(IN p_level char(1))

-> BEGIN

-> DECLARE p_num int DEFAULT 0;

-> CASE p_level

-> WHEN 'A' THEN

-> SET p_num=90;

-> WHEN 'B' THEN

-> SET p_num=80;

-> WHEN 'C' THEN

-> SET p_num=70;

-> WHEN 'D' THEN

-> SET p_num=60;

-> ELSE

-> SET p_num=0;

-> END CASE;

-> SELECT * FROM stu_score sc,stu s

-> WHERE sc.STU_ID=s.STU_ID AND sc.STU_SCORE >= p_num;

-> END//

Query OK, 0 rows affected (0.01 sec)

以上执行结果证明存储过程创建完成,接下来调用存储过程,返回等级D的学生信息,SQL语句如下所示。

mysql> delimiter ;

mysql> CALL SP_SCHOLARSHIP_LEVEL3('D');

+--------+-----------+--------+----------+-----------+---------+---------+

| stu_id | stu_score | STU_ID | STU_NAME | STU_CLASS | STU_SEX | STU_AGE |

+--------+-----------+--------+----------+-----------+---------+---------+

| 1 | 91 | 1 | aa | 3 | 女 | 23 |

| 2 | 62 | 2 | bb | 1 | 男 | 12 |

| 4 | 95 | 4 | dd | 2 | 男 | 22 |

| 5 | 71 | 5 | ee | 1 | 女 | 23 |

| 6 | 82 | 6 | ff | 2 | 女 | 13 |

| 7 | 60 | 7 | gg | 3 | 男 | 10 |

| 9 | 99 | 9 | ii | 1 | 男 | 13 |

+--------+-----------+--------+----------+-----------+---------+---------+

7 rows in set (0.08 sec)

Query OK, 0 rows affected (0.11 sec)

以上执行结果可以看出,通过调用存储过程SP_SCHOLARSHIP_LEVEL3,传入参数等级,成功查询到分数等级D的学生信息。

3.WHILE语句

WHILE语句可以创建一个带条件判断的循环过程,在语句执行时,先对指定的表达式进行判断,如果为真,则执行循环内的语句,否则退出循环,具体语法格式如下所示。

[begin_label:]

while search_condition do

      Statement_list

End while

[end_label]

以上语法格式中,search_condition为进行判断的表达式,如果表达式结果为真,WHILE语句内的语句或语句块被执行,直到search_condition为假,退出循环,接下来通过具体案例演示WHILE语句的用法。

例9-12​ 使用WHILE语句编写存储过程,通过传入的参数,当参数大于result时,循环计算参数乘以result并累加值,SQL语句如下所示。

mysql> delimiter //

mysql> CREATE PROCEDURE sp_cal(IN p_num INT,OUT p_result INT)

-> BEGIN

-> SET p_result=1;

-> WHILE p_num > 1 DO

-> SET p_result = p_num * p_result;

-> SET p_num = p_num-1;

-> END WHILE;

-> END//

Query OK, 0 rows affected (0.03 sec)

以上执行结果证明存储过程创建完成,接下来调用存储过程,传入参数5,计算最后循环相乘累加的值,SQL语句如下所示。

mysql> delimiter ;

mysql> CALL sp_cal(5,@result);

Query OK, 0 rows affected (0.01 sec)

mysql> SELECT @result;

+---------+

| @result |

+---------+

| 120 |

+---------+

1 row in set (0.00 sec)

以上执行执行结果可以看出,通过调用存储过程sp_cal,传入参数5,成功计算出循环结束后的值为120。

9.2.10​ 事件调度器

事件调度器是MySQL5.1后新增的功能,可以将数据库按自定义的时间周期触发某种操作,可以理解为时间触发器。MySQL中事件调度器是默认关闭的,可以先查看是否已开启事件调度器,具体语法格式如下所示。

SELECT @@event_scheduler;

通过以上语法,查看当前MySQL中是否开启了事件调度器,SQL语句如下所示。

mysql> SELECT @@event_scheduler;

+-------------------+

| @@event_scheduler |

+-------------------+

| OFF |

+-------------------+

1 row in set (0.00 sec)

从以上执行结果可看出,当前MySQL中没有开启事件调度器,开启事件调度器的语法格式如下所示。

SET GLOBAL event_scheduler=ON;

通过以上语法开启事件调度器,SQL语句如下所示。

mysql> SET GLOBAL event_scheduler=ON;

Query OK, 0 rows affected (0.04 sec)

从以上执行结果可看出,事件调度器成功开启,再次查看当前MySQL中事件调度器是否开启,SQL语句如下所示。

mysql> SELECT @@event_scheduler;

+-------------------+
| @@event_scheduler |
+-------------------+
| ON |
+-------------------+

1 row in set (0.00 sec)

以上执行结果证明,当前MySQL中事件调度器已经开启,创建事件调度器的语法格式如下所示。

CREATE EVENT [IF NOT EXISTS] event_name

ON SCHEDULE schedule

[ON COMPLETION [NOT] PRESERVE]

[ENABLE | DISABLE]

[COMMENT 'comment']

DO sql_statement;

schedule:

AT TIMESTAMP [+ INTERVAL INTERVAL]

| EVERY INTERVAL [STARTS TIMESTAMP] [ENDS TIMESTAMP]

INTERVAL:

quantity {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE |

WEEK | SECOND | YEAR_MONTH

以上语法格式中,event_name代表创建的事件名称,schedule代表是执行计划,它有两个选项,第一是在某一时刻执行,第二是从某时到某时每隔一段时间执行,INTERVAL代表时间间隔,可以精确到秒,接下来通过具体案例演示事件调度器的使用,首先创建一张测试表test_event,SQL语句如下所示。

mysql> CREATE TABLE test_event(

-> id INT,

-> create_time DATETIME

-> );

Query OK, 0 rows affected (0.18 sec)

以上执行结果证明表test_event创建完成,接着创建事件调度器test_event_1,实现每隔5秒向test_event表插入一条记录,SQL语句如下所示。

mysql> CREATE EVENT test_event_1

-> ON SCHEDULE

-> EVERY 5 SECOND

-> DO

-> INSERT INTO test_event(id,create_time)

-> VALUES('test_event',now());

Query OK, 0 rows affected (0.09 sec)

以上执行结果证明事件调度器创建完成,此时可以查看表test_event中的数据,SQL语句如下所示。

mysql> SELECT * FROM test_event;

+------+---------------------+

| id | create_time |

+------+---------------------+

| 0 | 2017-12-04 17:31:15 |

| 0 | 2017-12-04 17:31:20 |

| 0 | 2017-12-04 17:31:25 |

+------+---------------------+

3 rows in set (0.05 sec)

从以上执行结果可看出,表test_event中已经插入了三条数据,三条数据的插入时间间隔为5秒,对于事件调度器,还有很多选项,例如指定事件开始时间和结束时间,或者指定某个时间执行一次,而不是循环执行,更详细的使用方法可以参考官方文档,这里就不再赘述。

9.3​ 本章小结

本章首先介绍了存储过程的概念,然后详细讲解了存储过程的相关操作,包括存储过程的创建、查看、修改、删除等,需要大家通过实践去熟练掌握

9.4​ 习题

1.思考题

(1) 请简述什么是存储过程。

(2) 请简述存储过程的优缺点。

(3) 请简述创建存储过程的语法格式。

(4) 请简述查看存储过程的三种方式。

(5) 请简述删除存储过程的语法格式。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。