目录:
一、PROCEDURE:
PROCEDURE ,事务,一个存储过程,实际上就是在服务器端直接在数据库中编写一段代码作运算,在服务器端进行高效的运算,运算结果直接返还给客户端。
它和 FUNCTION 一个明显的不同点是, FUNCTION 最后会有 RETURN 语句,返回运算结果, PROCEDURE 不允许有 RETURN 语句的,但是可以在参数表中指定返还数据。
PROCEDURE 编译完成后会存储在数据库中,需要调用的时候使用 CALL 语句对事务或者函数进行调用。编写 PROCEDURE 不仅可以避免重复编码,同时还可以提高计算效率。
二、CREATE PROCEDURE基本语法:
下面不妨先看一看 CREATE PROCEDURE 以及 CREATE FUNCTION 的语法:
1
CREATE
2
[
DEFINER = { user | CURRENT_USER }
]
3
PROCEDURE
sp_name (
[
proc_parameter[,...
]
])
4
[
characteristic ...
]
routine_body
5
CREATE
6
[
DEFINER = { user | CURRENT_USER }
]
7
FUNCTION
sp_name (
[
func_parameter[,...
]
])
8
RETURNS
type
9
[
characteristic ...
]
routine_body
10
proc_parameter:
11
[
IN | OUT | INOUT
]
param_name type
12
func_parameter:
13
param_name type
14
type:
15
Any
valid MySQL data type
16
characteristic:
17
COMMENT
'
string
'
18
|
LANGUAGE SQL
19
|
[
NOT
]
DETERMINISTIC
20
|
{
CONTAINS
SQL
|
NO SQL
|
READS SQL DATA
|
MODIFIES SQL DATA }
21
|
SQL SECURITY { DEFINER
|
INVOKER }
22
routine_body:
23
Valid SQL routine statement
其中有几个注意点是:
1, DEFINER 你可以用这个选项指定可以调用该 PROCEDURE 的用户,比如说允许本地的用户nero使用,那么可以指定为:DEFINER='nero'@'localhost',如果这个事务就是创建给当前用户使用的,那么可以指定为:DEFINER=CURRENT_USER。
2,事务安全性:characteristic中如果使用了SQL SECURITY,那么事务每次执行的时候,指定的安全上下文都会被执行,它们会检查当前执行这个事务的人是否拥有执行权限。
比如说下面这个小例子:
delimiter
//
--
指定临时分隔符
CREATE
DEFINER
=
'
nero
'
@
'
localhost
'
PROCEDURE
simpleTest(OUT outParam
int
,
IN
inParam
int
) SQL SECURITY INVOKE
BEGIN
SELECT
COUNT
(
*
)
INTO
outParam
FROM
tbl
WHERE
col
<
inParam;
END
;
delimter ;
像上面这个例子,我们在定义的时候启用了 “SQL SECURITY INVOKE”, 只有是:a,对这个事务有调用权限;b,对这个表tbl有select权限的用户才能成功执行该 PROCEDURE 。
而在形参部分,则是通过 OUT 和 IN 指明参数传入还是传出,如果某个参数在传入之后要作为结果传出,那么不需要作特定指示,直接写明参数名称和参数类型即可。
调用这个事务则用 CALL 表达式即可:
SET
@b
=
100
;
CALL simpleTest(
@a
,
@b
);
SELECT
@a
;
--
显示结果
三、PROCEDURE小进阶:
知道PROCEDURE的基本语法以后,学习一下编写一个PROCEDURE经常需要用到的语句,分别有: DECLARE 声明语句, SET 设值语句, DECLARE...HANDLER 句柄声明语句, DECLARE...CURSOR 游标声明语句;条件判断 IF 和 CASE ;三种循环体: LOOP,REPEAT,WHILE 。
3.1、基本的DECLARE语句:
DECLARE基本语法:
DECLARE
var_name
[
, var_name
]
... type
[
DEFAULT value
]
比如说在某个事务中声明几个临时变量:
CREATE
PROCEDURE
test()
BEGIN
DECLARE
usrID
INT
;
DECLARE
usrName
VARCHAR
(
10
)
DEFAULT
'
NERO
'
;
..........
--
一些事务操作
END
;
3.2、声明HANDLER句柄:
基本语法:
1
DECLARE
handler_type HANDLER
2
FOR
condition_value
[
, condition_value
]
...
3
statement
4
handler_type:
5
CONTINUE
6
|
EXIT
7
|
UNDO
8
condition_value:
9
SQLSTATE
[
VALUE
]
sqlstate_value
10
|
condition_name
11
|
SQLWARNING
12
|
NOT
FOUND
13
|
SQLEXCEPTION
14
|
mysql_error_code
句柄的作用,就是在condition_value中,如果指定的任意条件出现了,那么statement这里的指定语句就会被执行。conditions条件有几种类型:
1、SQLSTATE指的是当前SQL返回的状态,这个对应的状态就比较多了,比如状态Error: 1169 SQLSTATE: 23000,指的是”因特定限制而导致的无法写入的错误“;Error: 1162 SQLSTATE: 42000 ,指的是”结果字符串超过了最大限制“。相关的状态代码请自行查阅帮助文档的” Server Error Codes and Messages “词条。
2、SQLWARNING,但凡是SQL发出的警告信息。
3、NOT FOUND,一般来说出现在SELECT语句中,游标触底;
4、SQLEXCEPTION,SQL错误。
不同的结果分别对应:
1、CONTINUE,如果条件成立,那么,在执行句柄的statement之后再继续执行程序,比如说下面这个例子:
1
CREATE
TABLE
tbl(col
INT
,
PRIMARY
KEY
(col));
2
3
delimiter
//
4
5
CREATE
PROCEDURE
HANDLER_DEMO()
6
BEGIN
7
DECLARE
CONTINUE
HANDLER
FOR
SQLSTATE
'
23000
'
SET
@x2
=
1
;
8
SET
@x
=
1
;
9
INSERT
INTO
tbl
VALUES
(
1
);
10
SET
@x
=
2
;
11
INSERT
INTO
tbl
VALUES
(
1
);
--
触发句柄的statement执行
12
SET
@x
=
3
;
13
END
;
14
//
15
16
CALLL HANDLER_DEMO()
//
17
18
SELECT
@x
//
19
SELECT
@x2
//
20
21
delimiter ;
结果当然是 @x为3,@x2为1 了。在代码11行,重复插入相同的值到主键上触发了23000错误,因而执行statement: SET @x2 = 1 ,然后再继续执行主程序的 SET @x = 3 .
2、EXIT,一旦条件被触发,当前BEGIN...END闭合语句将会终止执行,比如说:
1
delimiter
//
2
CREATE
PROCEDURE
EXIT_DEMO()
3
BEGIN
4
BEGIN
5
DECLARE
EXIT
HANDLER
FOR
SQLSTATE
'
23000
'
6
.......
7
END
;
8
END
;
9
delimiter ;
上述代码中,一旦出现23000错误,代码行4到7的BEGIN...END闭合语句立刻终止执行。
3.3、声明CURSOR游标 :
声明一个CURSOR游标:
DECLARE
cursor_name
CURSOR
FOR
select_statement
比如说最基本的:
DECLARE
cur1
CURSOR
FOR
SELECT
id,data
FROM
tbl;
此时cur1表示的即是SELECT语句返回的首个结果,有点类似于指针。
下面不妨看一个比较完整的例子:
1
delimiter
//
2
CREATE
PROCEDURE
CURSOR_DEMO()
3
BEGIN
4
DECLARE
done
INT
DEFAULT
0
;
--
INT型值,默认为0
5
DECLARE
a
CHAR
(
5
);
6
DECLARE
b,c
INT
;
7
/*
声明两个游标
*/
8
DECLARE
CUR1
CURSOR
FOR
SELECT
ID,DATA
FROM
tbl1;
9
DECLARE
CUR2
CURSOR
FOR
SELECT
I
FROM
tbl2;
10
/*
声明CONTINUE句柄,当游标触底时被触发
*/
11
DECLARE
CONTINUE
HANDLER
FOR
NOT
FOUND
SET
done
=
1
;
12
13
/*
打开游标
*/
14
OPEN
CUR1;
15
OPEN
CUR2;
16
17
/*
循环插入数据,使用REPEAT...UNTIL语句
*/
18
REPEAT
19
FETCH
CUR1
INTO
a,b;
--
读取游标中的数据,并移向下一行
20
FETCH
CUR2
INTO
c;
21
IF
NOT
done
THEN
--
当done为0的时候条件成立
22
IF
b
<
c
THEN
--
取b和c的较小者插入表3
23
INSERT
INTO
tbl3
VALUES
(a,b);
24
ELSE
25
INSERT
INTO
tbl3
VALUES
(a,c);
26
END
IF
;
27
END
IF
;
--
当游标触底,句柄将被触发,done值被设为1,然后从这里继续执行主程序
28
UNTIL done
--
句柄触发后,done为1,执行UNTIL
29
END
REPEAT;
30
31
/*
使用完毕,关闭游标
*/
32
CLOSE
CUR1;
33
CLOSE
CUR2;
34
END
//
35
36
delimiter ;
其中,FETCH语句的基本语法如下:
FETCH
cursor_name
INTO
var_name
[
, var_name
]
...
该语句每次都会返回SELECT结果中的下一行(如果有的话)。
3.4、循环语句:
循环涉及到的语句有:1、 LOOP、ITERATE和LEAVE ;2、 REPEAT ;3、 WHILE 。
下面直接给出对应的循环例子:
1、 LOOP、ITERATE和LEAVE:
/*
LOOP,ITERATE,LEAVE
*/
delimiter
//
CREATE
PROCEDURE
LOOP_DEMO(param
INT
)
BEGIN
label1: LOOP
SET
param
=
param
+
1
;
IF
param
<
100
THEN
ITERATE label1;
--
回到标签开始处
END
IF
;
LEAVE label1;
--
离开标签,退出流控制结构
END
LOOP label1;
--
结束循环
END
;
delimiter ;
2、REPEAT:
先给出REPEAT语法定义:
1
[
begin_label:] REPEAT
2
statement_list
3
UNTIL search_condition
4
END
REPEAT
[
end_label
]
可见,同样是可以在代码开始处插入label标签,不过REPEAT循环是自己有控制条件的,最好能直接使用UNTIL来进行条件判断。
比如下面这个例子:
1
delimiter
//
2
CREATE
PROCEDURE
REPEAT_DEMO
(param
INT
)
3
BEGIN
4
SET
@x
=
0
;
5
REPEAT
6
SET
@x
=
@x
+
1
;
7
UNTIL
@x
>
param
8
END
REPEAT;
9
END
//
10
11
delimiter ;
3、WHILE:
WHILE循环语法定义如下:
[
begin_label:
]
WHILE
search_condition DO
statement_list
END
WHILE
[
end_label
]
比如下面这个小例子:
delimiter
//
CREATE
PROCEDURE
WHILE_DEMO()
BEGIN
SET
param
INT
DEFAULT
10
;
WHILE
param
<
1000
....
--
循环内书写具体需要处理的事务
SET
param
=
param
+
100
;
END
WHILE
;
END
;
delimiter ;
四、顺带提一下触发器TRIGGER:
触发器都是和某个特定的表相关联的,对该表设定触发器以后,一旦对这个表进行了某个特定操作(诸如INSERT,UPDATE,DELETE),触发器就会被触发。
先给出CREATE TRIGGER语法定义:
CREATE
[
DEFINER = { user | CURRENT_USER }
]
TRIGGER
trigger_name trigger_time trigger_event
ON
tbl_name
FOR
EACH ROW trigger_body
同样的,可以通过DEFINER自行指定触发器的适用对象。
在trigger_time中可以指定触发时间(诸如:BEFORE,AFTER),trigger_event前面已经提到过了,另外,DROP TABLE或TRUNCATE TABLE这种操作是不会触发TRIGGER的。
下面给出个小例子:
1
delimiter
//
2
CREATE
DEFINER
'
nero
'
@
'
localhost
'
TRIGGER
trigger_demo
3
BEFORE
INSERT
ON
tbl1
FOR
EACH ROW
4
BEGIN
5
INSERT
INTO
tbl2
VALUES
(...........);
--
INSERT操作
6
DELETE
FROM
tbl3
WHERE
.......... ;
--
删除操作
7
UPDATE
tbl4
SET
col1
=
...... ;
--
更新操作
8
END
;
//
9
delimiter ;
这样,一旦本地用户nero对表tbl1进行INSERT操作的时候(之前,这里设置的是BEFORE),BEGIN...END内的内容就会被执行。

