Mysql完全支持SQL 2003标准。某些数据库(如DB2、Mimer)同样支持。但也有部分不支持的,如Oracle、SQL Server不支持。我们将会给予足够帮助和工具,使为其他DBMS编写的代码能更容易转移到Mysql上。 Setting up with MySQL 5.0 设置并开始MySQL 5.0服务
这是我启动mysql客户端的方式。你也许会使用其他方式,如果你使用的是二进制版本或者是Windows系统的电脑,你可能会在其他子目录下运行以下程序: easy@phpv:~> /usr/local/mysql/bin/mysql --user=root Welcome to the MySQL monitor. Commands end with ; or g. Your MySQL connection id is 1 to server version: 5.0.3-alpha-debug Type 'help;' or 'h' for help. Type 'c' to clear the buffer. 在演示中,我将会展示以root身份登陆后的mysql客户端返回的结果,这样意味着我有极大的特权。转载请注明翻译者陈朋奕及转自:www.phpv.net
Check for the Correct Version 核对版本 为了确认使用的MySQL的版本是正确的,我们要查询版本。我有两种方法确认我使用的是5.0版本: SHOW VARIABLES LIKE 'version'; or SELECT VERSION();
例如: mysql> SHOW VARIABLES LIKE 'version'; +---------------+-------------------+ | Variable_name | Value | +---------------+-------------------+ | version | 5.0.3-alpha-debug | +---------------+-------------------+ 1 row in set (0.00 sec) mysql> SELECT VERSION(); +-------------------+ | VERSION() | +-------------------+ | 5.0.3-alpha-debug | +-------------------+ 1 row in set (0.00 sec) 当看见数字'5.0.x' 后就可以确认存储过程能够在这个客户端上正常工作。 The Sample "Database" 示例数据库 现在要做的第一件事是创建一个新的数据库然后设定为默认数据库实现这个步骤的SQL 语句如下: CREATE DATABASE db5; USE db5; 例如: mysql> CREATE DATABASE db5; Query OK, 1 row affected (0.00 sec) mysql> USE db5; Database changed 在这里要避免使用有重要数据的实际的数据库然后我们创建一个简单的工作表。 实现这个步骤的SQL 语句如下: mysql> CREATE DATABASE db5; Query OK, 1 row affected (0.01 sec) mysql> USE db5; Database changed mysql> CREATE TABLE t (s1 INT); Query OK, 0 rows affected (0.01 sec) mysql> INSERT INTO t VALUES (5); Query OK, 1 row affected (0.00 sec) 你会发现我只在表中插入了一列。这样做的原因是我要保持表的简单,因为在这里并不需要展示查询数据的技巧,而是教授存储过程,不需要使用大的数据表,因为它本身已经够复杂了。 这就是示例数据库,我们将从这个名字为t的只包含一列的表开始Pick a Delimiter 选择分隔符 现在我们需要一个分隔符,实现这个步骤的SQL语句如下: DELIMITER // 例如: mysql> DELIMITER // 分 隔符是你通知mysql客户端你已经完成输入一个SQL语句的字符或字符串符号。一直以来我们都使用分号“;”,但在存储过程中,这会产生不少问题,因为 存储过程中有许多语句,所以每一个都需要一个分号因此你需要选择一个不太可能出现在你的语句或程序中的字符串作为分隔符。我曾用过双斜杠“//”,也有人 用竖线“|”。我曾见过在DB2程序中使用“@”符号的,但我不喜欢这样。你可以根据自己的喜好来选择,但是在这个课程中为了更容易理解,你最好选择跟我 一样。如果以后要恢复使用“;”(分号)作为分隔符,输入下面语句就可以了: "DELIMITER ;//". CREATE PROCEDURE Example 创建程序实例 CREATE PROCEDURE p1 () SELECT * FROM t; // 也许这是你使用Mysql创建的第一个存储过程。假如是这样的话,最好在你的日记中记下这个重要的里程碑。 CREATE PROCEDURE p1 () SELECT * FROM t; // <-- SQL语句存储过程的第一部分是“CREATE PROCEDURE”: CREATE PROCEDURE p1 () SELECT * FROM t; // <-- 第二部分是过程名,上面新存储过程的名字是p1。 Digression: Legal Identifiers 题外话:合法标识符的问题 存储过程名对大小写不敏感,因此‘P1’和‘p1’是同一个名字,在同一个数据库中你将不能给两个存储过程取相同的名字,因为这样将会导致重载。某些DBMS允许重载(Oracle支持),但是MySQL不支持(译者话:希望以后会支持吧。)。
你可以采取“数据库名.存储过程名”这样的折中方法,如“db5.p1”。存储过程名可以分开,它可以包括空格符,其长度限制为64个字符,但注意不要使用MySQL内建函数的名字,如果这样做了,在调用时将会出现下面的情况: mysql> CALL pi(); Error 1064 (42000): You have a syntax error. mysql> CALL pi (); Error 1305 (42000): PROCEDURE does not exist.
在上面的第一个例子里,我调用的是一个名字叫pi的函数,但你必须在调用的函数名后加上空格,就像第二个例子那样。 CREATE PROCEDURE p1 () SELECT * FROM t; // <-- 其中“()”是“参数列表”。 CREATE PROCEDURE 语句的第三部分是参数列表。通常需要在括号内添加参数。例子中的存储过程没有参数,因此参数列表是空的—所以我只需要键入空括号,然而这是必须的。 CREATE PROCEDURE p1 () SELECT * FROM t; // <-- "SELECT * FROM t;" 是存储过程的主体。 然后到了语句的最后一个部分了,它是存储过程的主体,是一般的SQL语句。过程体中语句 "SELECT * FROM t;" 包含一个分号,如果后面有语句结束符号(//)时可以不写这个分号。 如果你还记得我把这部分叫做程序的主体将会是件好事,因为(body)这个词是大家使用的技术上的术语。通常我们不会将SELECT语句用在存储过程中,这里只是为了演示。所以使用这样的语句,能在调用时更好的看出程序是否正常工作。 ySQL 5.0 新特性教程 第一部分存储过程:第二讲 Why MySQL Statements are Legal in a Procedure Body 什么MySQL语句在存储过程体中是合法的?
什么样的SQL语句在Mysql存储过程中才是合法的呢?你可以创建一个包含INSERT, UPDATE,DELETE, SELECT, DROP, CREATE, REPLACE等的语句。你唯一需要记住的是如果代码中包含MySQL扩充功能,那么代码将不能移植。在标准SQL语句中:任何数据库定义语言都是合法 的,如: CREATE PROCEDURE p () DELETE FROM t; // SET、COMMIT以及ROLLBACK 也是合法的,如: CREATE PROCEDURE p () SET @x = 5; // MySQL的附加功能:任何数据操作语言的语句都将合法。 CREATE PROCEDURE p () DROP TABLE t; // MySQL扩充功能:直接的SELECT也是合法的: CREATE PROCEDURE p () SELECT 'a'; // 顺便提一下,我将存储过程中包括DDL语句的功能称为MySQL附加功能的原因是在SQL标准中把这个定义为非核心的,即可选组件。转载请注明翻译者陈朋奕及转自:www.phpv.net
在过程体中有一个约束,就是不能有对例程或表操作的数据库操作语句。例如下面的例子就是非法的: CREATE PROCEDURE p1 () CREATE PROCEDURE p2 () DELETE FROM t; // 下面这些对MySQL 5.0来说全新的语句,过程体中是非法的?lt;BR>CREATE PROCEDURE, ALTER PROCEDURE, DROP PROCEDURE, CREATE FUNCTION, DROP FUNCTION, CREATE TRIGGER, DROP TRIGGER. 不过你可以使用 "CREATE PROCEDURE db5.p1 () DROP DATABASE db5//" ,但是类似 "USE database" 语句也是非法的,因为MySQL假定默认数据库就是过程的工作场所。 Call the Procedure 调用存储过程 1. 现在我们就可以调用一个存储过程了,你所需要输入的全部就是CALL和你过程名以及一个括号再一次强调,括号是必须的当你调用例子里面的p1过程时,结果是屏幕返回了t表的内容 mysql> CALL p1() // +------+ | s1 | +------+ | 5 | +------+ 1 row in set (0.03 sec) Query OK, 0 rows affected (0.03 sec) 因为过程中的语句是 "SELECT * FROM t;"
2. Let me say that again, another way. 其他实现方式 mysql> CALL p1() // 和下面语句的执行效果一样: mysql> SELECT * FROM t; // 所以,你调用p1过程就相当于你执行了下面语句: "SELECT * FROM t;". 好了,主要的知识点"创建和调用过程方法"已经清楚了。我希望你能对自己说这相当简单。但是很快我们就有一系列的练习,每次都加一条子句,或者改变已经存在的子句。那样在写复杂部件前我们将会有很多可用的子句。转载请注明翻译者陈朋奕及转自:www.phpv.net Characteristics Clauses 特征子句 1.
CREATE PROCEDURE p2 () LANGUAGE SQL <-- NOT DETERMINISTIC <-- SQL SECURITY DEFINER <-- COMMENT 'A Procedure' <-- SELECT CURRENT_DATE, RAND() FROM t //
CREATE PROCEDURE p7 () BEGIN SET @a = 5; SET @b = 5; INSERT INTO t VALUES (@a); SELECT s1 * @a FROM t WHERE s1 >= @b; END; // /* I won't CALL this. 这个语句将不会被调用 */
The New SQL Statements 新SQL语句 Variables 变量 在复合语句中声明变量的指令是DECLARE。 (1) Example with two DECLARE statements 两个DECLARE语句的例子 CREATE PROCEDURE p8 () BEGIN DECLARE a INT; DECLARE b INT; SET a = 5; SET b = 5; INSERT INTO t VALUES (a); SELECT s1 * a FROM t WHERE s1 >= b; END; // /* I won't CALL this */
(2) Example with no DEFAULT clause and SET statement 没有默认子句和设定语句的例子 CREATE PROCEDURE p9 () BEGIN DECLARE a INT /* there is no DEFAULT clause */; DECLARE b INT /* there is no DEFAULT clause */; SET a = 5; /* there is a SET statement */ SET b = 5; /* there is a SET statement */ INSERT INTO t VALUES (a); SELECT s1 * a FROM t WHERE s1 >= b; END; // /* I won't CALL this */ 有很多初始化变量的方法。如果没有默认的子句,那么变量的初始值为NULL。你可以在任何时候使用SET语句给变量赋值。
(3) Example with DEFAULT clause 含有DEFAULT子句的例子 CREATE PROCEDURE p10 () BEGIN DECLARE a, b INT DEFAULT 5; INSERT INTO t VALUES (a); SELECT s1 * a FROM t WHERE s1 >= b; END; // 我们在这里做了一些改变,但是结果还是一样的。在这里使用了DEFAULT子句来设定初 始值,这就不需要把DECLARE和SET语句的实现分开了。 (4) Example of CALL 调用的例子 mysql> CALL p10() // +--------+ | s1 * a | +--------+ | 25 | | 25 | +--------+ 2 rows in set (0.00 sec) Query OK, 0 rows affected (0.00 sec) 结果显示了过程能正常工作
Conditions and IF-THEN-ELSE 条件式和IF-THEN-ELSE 1. 现在我们可以写一些包含条件式的例子: CREATE PROCEDURE p12 (IN parameter1 INT) BEGIN DECLARE variable1 INT; SET variable1 = parameter1 + 1; IF variable1 = 0 THEN INSERT INTO t VALUES (17); END IF; IF parameter1 = 0 THEN UPDATE t SET s1 = s1 + 1; ELSE UPDATE t SET s1 = s1 + 2; END IF; END; // 这里是一个包含IF语句的过程。里面有两个IF语句,一个是IF语句END IF,另一个是IF语句ELSE语句END IF。我们可以在这里使用复杂的过程,但我会尽量使其简单让你能更容易弄清楚。 2. CALL p12 (0) // 我们调用这个过程,传入值为0,这样parameter1的值将为0。
3. CREATE PROCEDURE p12 (IN parameter1 INT) BEGIN DECLARE variable1 INT; SET variable1 = parameter1 + 1; <-- IF variable1 = 0 THEN INSERT INTO t VALUES (17); END IF; IF parameter1 = 0 THEN UPDATE t SET s1 = s1 + 1; ELSE UPDATE t SET s1 = s1 + 2; END IF; END; // 这里变量variable1被赋值为parameter1加1的值,所以执行后变量variable1为1。 4. CREATE PROCEDURE p12 (IN parameter1 INT) BEGIN DECLARE variable1 INT; SET variable1 = parameter1 + 1; IF variable1 = 0 THEN <-- INSERT INTO t VALUES (17); END IF; IF parameter1 = 0 THEN UPDATE t SET s1 = s1 + 1; ELSE UPDATE t SET s1 = s1 + 2; END IF; END; // 因为变量variable1值为1,因此条件"if variable1 = 0"为假, IF …… END IF 被跳过,没有被执行。 5. CREATE PROCEDURE p12 (IN parameter1 INT) BEGIN DECLARE variable1 INT; SET variable1 = parameter1 + 1; IF variable1 = 0 THEN INSERT INTO t VALUES (17); END IF; IF parameter1 = 0 THEN <-- UPDATE t SET s1 = s1 + 1; ELSE UPDATE t SET s1 = s1 + 2; END IF; END; //
到第二个IF条件,判断结果为真,于是中间语句被执行了 6. CREATE PROCEDURE p12 (IN parameter1 INT) BEGIN DECLARE variable1 INT; SET variable1 = parameter1 + 1; IF variable1 = 0 THEN INSERT INTO t VALUES (17); END IF; IF parameter1 = 0 THEN UPDATE t SET s1 = s1 + 1; <-- ELSE UPDATE t SET s1 = s1 + 2; END IF; END; // 因为参数parameter1值等于0,UPDATE语句被执行。如果parameter1值为NULL,则下一条 UPDATE 语句将被执行现在表t中有两行,他们都包含值5,所以如果我们调用p12,两行的值会变成6。 7. mysql> CALL p12(0)// Query OK, 2 rows affected (0.28 sec) mysql> SELECT * FROM t// +------+ | s1 | +------+ | 6 | | 6 | +------+ 2 rows in set (0.01 sec) 结果也是我们所期望的那样。 CASE 指令 1. CREATE PROCEDURE p13 (IN parameter1 INT) BEGIN DECLARE variable1 INT; SET variable1 = parameter1 + 1; CASE variable1 WHEN 0 THEN INSERT INTO t VALUES (17); WHEN 1 THEN INSERT INTO t VALUES (18); ELSE INSERT INTO t VALUES (19); END CASE; END; // 如果需要进行更多条件真假的判断我们可以使用CASE语句。CASE语句使用和IF一样简单。 我们可以参考上面的例子: 2. mysql> CALL p13(1)// Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM t// +------+ | s1 | +------+ | 6 | | 6 | | 19 | +------+ 3 rows in set (0.00 sec) 执行过程后,传入值1,如上面例子,值19被插入到表t中。 Question 问题 问题: CALL p13(NULL) //的作用是什么? 另一个:这个CALL语句做了那些动作? 你可以通过执行后观察SELECT做了什么,也可以根据代码判断,在5秒内做出。 Answer 答案 mysql> CALL p13(NULL)// Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM t// +------+ | s1 | +------+ | 6 | | 6 | | 19 | | 19 | +------+ 4 rows in set (0.00 sec) 答案是当你调用p13时,MySQL插入了另一条包含数值19的记录。原因是变量variable1的值为NULL,CASE语句的ELSE部分就被执行了。希望这对大家有意义。如果你回答不出来,没有问题,我们可以继续向下走。
Loops 循环语句 WHILE ... END WHILE LOOP ... END LOOP REPEAT ... END REPEAT GOTO 下面我们将会创建一些循环。我们有三种标准的循环方式: WHILE循环,LOOP循环以及REPEAT循环。还有一种非标准的循环方式:GO TO(译者语:最好不要用吧,用了就使流程混乱)。 WHILE ... END WHILE CREATE PROCEDURE p14 () BEGIN DECLARE v INT; SET v = 0; WHILE v < 5 DO INSERT INTO t VALUES (v); SET v = v + 1; END WHILE; END; // 这是WHILE循环的方式。我很喜欢这种方式,它跟IF语句相似,因此不需要掌握很多新的语法。这里的INSERT和SET语句在WHILE和END WHILE之间,当变量v大于5的时候循环将会退出。使用 "SET v = 0;" 语句使为了防止一个常见的错误,如果没有初始化,默认变量值为NULL,而NULL和任何值操作结果都为NULL。 WHILE ... END WHILE example mysql> CALL p14()// Query OK, 1 row affected (0.00 sec) 以上就是调用过程p14的结果不用关注系统返回是"one row affected"还是"five rows affected",因为这里的计数只对最后一个INSERT动作进行计数。
WHILE ... END WHILE example: CALL mysql> select * from t; // +------+ | s1 | +------+ .... | 0 | | 1 | | 2 | | 3 | | 4 | +------+ 9 rows in set (0.00 sec) 调用后可以看到程序向数据库中插入了5行。 REPEAT ... END REPEAT CREATE PROCEDURE p15 () BEGIN DECLARE v INT; SET v = 0; REPEAT INSERT INTO t VALUES (v); SET v = v + 1; UNTIL v >= 5 END REPEAT; END; // 这是一个REPEAT循环的例子,功能和前面WHILE循环一样。区别在于它在执行后检查结果,而WHILE则是执行前检查。(译者语:可能等同于DO WHILE吧)
REPEAT ... END REPEAT: look at the UNTIL: UNTIL的作用 CREATE PROCEDURE p15 () BEGIN DECLARE v INT; SET v = 0; REPEAT INSERT INTO t VALUES (v); SET v = v + 1; UNTIL v >= 5 <-- END REPEAT; END; // 注意到UNTIL语句后面没有分号,在这里可以不写分号,当然你加上额外的分号更好。
REPEAT ... END REPEAT: calling :调用 mysql> CALL p15()// Query OK, 1 row affected (0.00 sec) mysql> SELECT COUNT(*) FROM t// +----------+ | COUNT(*) | +----------+ | 14 | +----------+ 1 row in set (0.00 sec) 我们可以看到调用p15过程后又插入了5行记录
LOOP ... END LOOP CREATE PROCEDURE p16 () BEGIN DECLARE v INT; SET v = 0; loop_label: LOOP INSERT INTO t VALUES (v); SET v = v + 1; IF v >= 5 THEN LEAVE loop_label; END IF; END LOOP; END; // 以上是LOOP循环的例子。 LOOP循环不需要初始条件,这点和WHILE循环相似,同时它又和REPEAT循环一样也不需要结束条件。
LOOP ... END LOOP: with IF and LEAVE 包含IF和LEAVE的LOOP循环 CREATE PROCEDURE p16 () BEGIN DECLARE v INT; SET v = 0; loop_label: LOOP INSERT INTO t VALUES (v); SET v = v + 1; IF v >= 5 THEN <-- LEAVE loop_label; END IF; END LOOP; END; // 在循环内部加入IF语句,在IF语句中包含LEAVE语句。这里LEAVE语句的意义是离开循环。 LEAVE的语法是LEAVE加循环语句标号,关于循环语句的标号问题我会在后面进一步讲解。 LOOP ... END LOOP: calling :调用 mysql> CALL p16()// Query OK, 1 row affected (0.00 sec) mysql> SELECT COUNT(*) FROM t// +----------+ | COUNT(*) | +----------+ | 19 | +----------+ 1 row in set (0.00 sec) 调用过程p16后,结果是另5行被插入表t中。
Labels 标号 CREATE PROCEDURE p17 () label_1: BEGIN label_2: WHILE 0 = 1 DO LEAVE label_2; END WHILE; label_3: REPEAT LEAVE label_3; UNTIL 0 =0 END REPEAT; label_4: LOOP LEAVE label_4; END LOOP; END; // 最后一个循环例子中我使用了语句标号。现在这里有一个包含4个语句标号的过程的例子。我们可以在BEGIN、 WHILE、REPEAT或者LOOP语句前使用语句标号,语句标号只能在合法的语句前面使用。因此"LEAVE label_3"意味着离开语句标号名定义为label_3的语句或复合语句。
End Labels 标号结束符 CREATE PROCEDURE p18 () label_1: BEGIN label_2: WHILE 0 = 1 DO LEAVE label_2; END WHILE label_2; label_3: REPEAT LEAVE label_3; UNTIL 0 =0 END REPEAT label_3 ; label_4: LOOP LEAVE label_4; END LOOP label_4 ; END label_1 ; // 你也可以在语句结束时使用语句标号,和在开头时使用一样。这些标号结束符并不是十分有用。 它们是可选的。如果你需要,他们必须和开始定义的标号名字一样当然为了有良好的编程习惯, 方便他人阅读,最好还是使用标号结束符。 LEAVE and Labels 跳出和标号 CREATE PROCEDURE p19 (parameter1 CHAR) label_1: BEGIN label_2: BEGIN label_3: BEGIN IF parameter1 IS NOT NULL THEN IF parameter1 = 'a' THEN LEAVE label_1; ELSE BEGIN IF parameter1 = 'b' THEN LEAVE label_2; ELSE LEAVE label_3; END IF; END; END IF; END IF; END; END; END;// LEAVE 语句使程序跳出复杂的复合语句。 ITERATE 迭代如果目标是ITERATE(迭代)语句的话,就必须用到LEAVE语句 CREATE PROCEDURE p20 () BEGIN DECLARE v INT; SET v = 0; loop_label: LOOP IF v = 3 THEN SET v = v + 1; ITERATE loop_label; END IF; INSERT INTO t VALUES (v); SET v = v + 1; IF v >= 5 THEN LEAVE loop_label; END IF; END LOOP; END; //
ITERATE: Walking through the loop 深入循环 CREATE PROCEDURE p20 () BEGIN DECLARE v INT; SET v = 0; loop_label: LOOP <-- IF v = 3 THEN SET v = v + 1; ITERATE loop_label; END IF; INSERT INTO t VALUES (v); SET v = v + 1; IF v >= 5 THEN LEAVE loop_label; END IF; END LOOP; END; // 让这个已经定义了标号的循环运行起来。
ITERATE: Walking through the loop CREATE PROCEDURE p20 () BEGIN DECLARE v INT; SET v = 0; loop_label: LOOP IF v = 3 THEN <-- SET v = v + 1; ITERATE loop_label; END IF; INSERT INTO t VALUES (v); SET v = v + 1; IF v >= 5 THEN LEAVE loop_label; END IF; END LOOP; END; // v的值变成3,然后我们把它增加到4。
ITERATE: walking through the loop CREATE PROCEDURE p20 () BEGIN DECLARE v INT; SET v = 0; loop_label: LOOP IF v = 3 THEN SET v = v + 1; ITERATE loop_label; <-- END IF; INSERT INTO t VALUES (v); SET v = v + 1; IF v >= 5 THEN LEAVE loop_label; END IF; END LOOP; END; // 然后开始ITERATE(迭代)过程。
ITERATE: walking through the loop CREATE PROCEDURE p20 () BEGIN DECLARE v INT; SET v = 0; loop_label: LOOP <-- IF v = 3 THEN SET v = v + 1; ITERATE loop_label; END IF; INSERT INTO t VALUES (v); SET v = v + 1; IF v >= 5 THEN LEAVE loop_label; END IF; END LOOP; END; // 这里的ITERATE(迭代)让循环又回到了循环的头部。
ITERATE: walking through the loop CREATE PROCEDURE p20 () BEGIN DECLARE v INT; SET v = 0; loop_label: LOOP IF v = 3 THEN SET v = v + 1; ITERATE loop_label; END IF; INSERT INTO t VALUES (v); SET v = v + 1; IF v >= 5 THEN LEAVE loop_label; <-- END IF; END LOOP; END; // 当v的值变为5时,程序将执行LEAVE语句
ITERATE: walking through the loop CREATE PROCEDURE p20 () BEGIN DECLARE v INT; SET v = 0; loop_label: LOOP IF v = 3 THEN SET v = v + 1; ITERATE loop_label; END IF; INSERT INTO t VALUES (v); SET v = v + 1; IF v >= 5 THEN LEAVE loop_label; END IF; END LOOP; END; // <-- LEAVE的结果就是跳出循环,使运行指令到达复合语句的最后一步。
mysql> CREATE PROCEDURE p9 () -> BEGIN -> DECLARE EXIT HANDLER FOR NOT FOUND BEGIN END; -> DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END; -> DECLARE EXIT HANDLER FOR SQLWARNING BEGIN END; -> END;// Query OK, 0 rows affected (0.00 sec)
这里是三个预声明的条件:NOT FOUND (找不到行), SQLEXCEPTION (错误),SQLWARNING ( 警告或注释)。因为它们是预声明的,因此不需要声明条件就可以使用。不过如果你去做这样的声明:"DECLARE SQLEXCEPTION CONDITION ...",你将会得到错误信息提示。
Cursors 游标
游标实现功能摘要: DECLARE cursor-name CURSOR FOR SELECT ...; OPEN cursor-name; FETCH cursor-name INTO variable [, variable]; CLOSE cursor-name; 现在我们开始着眼游标了。虽然我们的存储过程中的游标语法还并没有完整的实现,但是 已经可以完成基本的事务如声明游标,打开游标,从游标里读取,关闭游标。
1. Cursor Example CREATE PROCEDURE p25 (OUT return_val INT) BEGIN DECLARE a,b INT; DECLARE cur_1 CURSOR FOR SELECT s1 FROM t; DECLARE CONTINUE HANDLER FOR NOT FOUND SET b = 1; OPEN cur_1; REPEAT FETCH cur_1 INTO a; UNTIL b = 1 END REPEAT; CLOSE cur_1; SET return_val = a; END;// 我们看一下包含游标的存储过程的新例子。
2. Cursor Example
CREATE PROCEDURE p25 (OUT return_val INT) BEGIN DECLARE a,b INT; <-- DECLARE cur_1 CURSOR FOR SELECT s1 FROM t; DECLARE CONTINUE HANDLER FOR NOT FOUND SET b = 1; OPEN cur_1; REPEAT FETCH cur_1 INTO a; UNTIL b = 1 END REPEAT; CLOSE cur_1; SET return_val = a; END;// 这个过程开始声明了三个变量。附带说一下,顺序是十分重要的。首先要进行变量声明, 然后声明条件,随后声明游标,再后面才是声明错误处理器。如果你没有按顺序声明, 系统会提示错误信息。
3. Cursor Example
CREATE PROCEDURE p25 (OUT return_val INT) BEGIN DECLARE a,b INT; DECLARE cur_1 CURSOR FOR SELECT s1 FROM t; <-- DECLARE CONTINUE HANDLER FOR NOT FOUND SET b = 1; OPEN cur_1; REPEAT FETCH cur_1 INTO a; UNTIL b = 1 END REPEAT; CLOSE cur_1; SET return_val = a; END;//
程序第二步声明了游标cur_1,如果你使用过嵌入式SQL的话,就知道这和嵌入式SQL差不多。
4. Cursor Example
CREATE PROCEDURE p25 (OUT return_val INT) BEGIN DECLARE a,b INT; DECLARE cur_1 CURSOR FOR SELECT s1 FROM t; DECLARE CONTINUE HANDLER FOR NOT FOUND <-- SET b = 1; <-- OPEN cur_1; REPEAT FETCH cur_1 INTO a; UNTIL b = 1 END REPEAT; CLOSE cur_1; SET return_val = a; END;//
最后进行的是错误处理器的声明。这个CONTINUE处理没有引用SQL错误代码和SQLSTATE值。 它使用的是NOT FOUND系统返回值,这和SQLSTATE 02000是一样的。 转载请注明翻译者陈朋奕及转自:www.phpv.net 5. Cursor Example CREATE PROCEDURE p25 (OUT return_val INT) BEGIN DECLARE a,b INT; DECLARE cur_1 CURSOR FOR SELECT s1 FROM t; DECLARE CONTINUE HANDLER FOR NOT FOUND SET b = 1; OPEN cur_1; <-- REPEAT FETCH cur_1 INTO a; UNTIL b = 1 END REPEAT; CLOSE cur_1; SET return_val = a; END;//
过程第一个可执行的语句是OPEN cur_1,它与SELECT s1 FROM t语句是关联的,过程将执行 SELECT s1 FROM t,返回一个结果集。
6. Cursor Example CREATE PROCEDURE p25 (OUT return_val INT) BEGIN DECLARE a,b INT; DECLARE cur_1 CURSOR FOR SELECT s1 FROM t; DECLARE CONTINUE HANDLER FOR NOT FOUND SET b = 1; OPEN cur_1; REPEAT FETCH cur_1 INTO a; <-- UNTIL b = 1 END REPEAT; CLOSE cur_1; SET return_val = a; END;//
CREATE PROCEDURE p25 (OUT return_val INT) BEGIN DECLARE a,b INT; DECLARE cur_1 CURSOR FOR SELECT s1 FROM t; DECLARE CONTINUE HANDLER FOR NOT FOUND SET b = 1; <-- OPEN cur_1; REPEAT FETCH cur_1 INTO a; UNTIL b = 1 END REPEAT; CLOSE cur_1; SET return_val = a; END;//
最后当MySQL的FETCH没有获得行时,CONTINUE处理被触发,将变量b赋值为1。
8. Cursor Example
CREATE PROCEDURE p25 (OUT return_val INT) BEGIN DECLARE a,b INT; DECLARE cur_1 CURSOR FOR SELECT s1 FROM t; DECLARE CONTINUE HANDLER FOR NOT FOUND SET b = 1; OPEN cur_1; REPEAT FETCH cur_1 INTO a; UNTIL b = 1 END REPEAT; CLOSE cur_1; <-- SET return_val = a; END;//
CREATE PROCEDURE p25 (OUT return_val INT) BEGIN DECLARE a,b INT; DECLARE cur_1 CURSOR FOR SELECT s1 FROM t; DECLARE CONTINUE HANDLER FOR NOT FOUND SET b = 1; OPEN cur_1; REPEAT FETCH cur_1 INTO a; UNTIL b = 1 END REPEAT; CLOSE cur_1; SET return_val = a; <-- END;// 这个例程中我们为输出参数指派了一个局部变量,这样在过程结束后的结果仍能使用。 转载请注明翻译者陈朋奕及转自:www.phpv.net 10. Cursor Example
CREATE PROCEDURE p25 (OUT return_val INT) BEGIN DECLARE a,b INT; DECLARE cur_1 CURSOR FOR SELECT s1 FROM t; DECLARE CONTINUE HANDLER FOR NOT FOUND SET b = 1; OPEN cur_1; REPEAT FETCH cur_1 INTO a; UNTIL b = 1 END REPEAT; CLOSE cur_1; SET return_val = a; END;//
GRANT SHOW ROUTINE ON db6.* TO joey [WITH GRANT OPTION];
因为我们已经有控制视图的特权了:GRANT SHOW VIEW。所以在这个基础上,为了保证兼容, 日后可能会添加GRANT SHOW ROUTINE特权。这样做是不太符合标准的,在写本书的时候,MySQL还没实现这个功能。
4. Privileges Invokers and Definers 特权调用者和定义者
CREATE PROCEDURE p26 () SQL SECURITY INVOKER SELECT COUNT(*) FROM t // CREATE PROCEDURE p27 () SQL SECURITY DEFINER SELECT COUNT(*) FROM t // GRANT INSERT ON db5.* TO peter; //