我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。
一个存储过程是一个可编程的函数,它在数据库中创建并保存。它可以有SQL语句和一些特殊的控制结构组成。当希望在不同的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是非常有用的。数据库中的存储过程可以看做是对编程中面向对象方法的模拟。它允许控制数据的访问方式。
存储过程通常有以下优点:
存储过程是数据库存储的一个重要的功能,但是MySQL在5.0以前并不支持存储过程,这使得MySQL在应用上大打折扣。好在MySQL 5.0终于开始已经支持存储过程,这样即可以大大提高数据库的处理速度,同时也可以提高数据库编程的灵活性。
(1). 格式
MySQL存储过程创建的格式:CREATE PROCEDURE 过程名 ([过程参数[,...]])
[特性 ...] 过程体
这里先举个例子:
mysql> DELIMITER //
mysql> CREATE PROCEDURE proc1(OUT s int)
-> BEGIN
-> SELECT COUNT(*) INTO s FROM user;
-> END
-> //
mysql> DELIMITER ;
注:
(2). 声明分割符
其实,关于声明分割符,上面的注解已经写得很清楚,不需要多说,只是稍微要注意一点的是:如果是用MySQL的Administrator管理工具时,可以直接创建,不再需要声明。
(3). 参数
MySQL存储过程的参数用在存储过程的定义,共有三种参数类型,IN,OUT,INOUT,形式如:
CREATE PROCEDURE([[IN |OUT |INOUT ] 参数名 数据类形...])
IN 输入参数: 表示该参数的值必须在调用存储过程时指定,在存储过程中修改该参数的值不能被返回,为默认值
OUT 输出参数: 该值可在存储过程内部被改变,并可返回
INOUT 输入输出参数: 调用时指定,并且可被改变和返回
(1).字符串类
CHARSET(str) //返回字串字符集
CONCAT (string2 [,... ]) //连接字串
INSTR (string ,substring ) //返回substring首次在string中出现的位置,不存在返回0
LCASE (string2 ) //转换成小写
LEFT (string2 ,length ) //从string2中的左边起取length个字符
LENGTH (string ) //string长度
LOAD_FILE (file_name ) //从文件读取内容
LOCATE (substring , string [,start_position ] ) 同INSTR,但可指定开始位置
LPAD (string2 ,length ,pad ) //重复用pad加在string开头,直到字串长度为length
LTRIM (string2 ) //去除前端空格
REPEAT (string2 ,count ) //重复count次
REPLACE (str ,search_str ,replace_str ) //在str中用replace_str替换search_str
RPAD (string2 ,length ,pad) //在str后用pad补充,直到长度为length
RTRIM (string2 ) //去除后端空格
STRCMP (string1 ,string2 ) //逐字符比较两字串大小,
SUBSTRING (str , position [,length ]) //从str的position开始,取length个字符,
TRIM([[BOTH|LEADING|TRAILING] [padding] FROM]string2) //去除指定位置的指定字符
UCASE (string2 ) //转换成大写
RIGHT(string2,length) //取string2最后length个字符
SPACE(count) //生成count个空格
注:mysql中处理字符串时,默认第一个字符下标为1,即参数position必须大于等于1
mysql> select substring('abcd',0,2);
+-----------------------+
| substring('abcd',0,2) |
+-----------------------+
| |
+-----------------------+
1 row in set (0.00 sec)
mysql> select substring('abcd',1,2);
+-----------------------+
| substring('abcd',1,2) |
+-----------------------+
| ab |
+-----------------------+
1 row in set (0.02 sec)
(2).数学类
ABS (number2 ) //绝对值
BIN (decimal_number ) //十进制转二进制
CEILING (number2 ) //向上取整
CONV(number2,from_base,to_base) //进制转换
FLOOR (number2 ) //向下取整
FORMAT (number,decimal_places ) //保留小数位数
HEX (DecimalNumber ) //转十六进制
注:HEX()中可传入字符串,则返回其ASC-11码,如HEX('DEF')返回4142143
也可以传入十进制整数,返回其十六进制编码,如HEX(25)返回19
LEAST (number , number2 [,..]) //求最小值
MOD (numerator ,denominator ) //求余
POWER (number ,power ) //求指数
RAND([seed]) //随机数
ROUND (number [,decimals ]) //四舍五入,decimals为小数位数]
SIGN (number2 ) //
注:返回类型并非均为整数,如:
(1)默认变为整形值
mysql> select round(1.23);
+-------------+
| round(1.23) |
+-------------+
| 1 |
+-------------+
1 row in set (0.00 sec)
mysql> select round(1.56);
+-------------+
| round(1.56) |
+-------------+
| 2 |
+-------------+
1 row in set (0.00 sec)
(2)可以设定小数位数,返回浮点型数据
mysql> select round(1.567,2);
+----------------+
| round(1.567,2) |
+----------------+
| 1.57 |
+----------------+
1 row in set (0.00 sec)
(3).日期时间类
ADDTIME (date2 ,time_interval ) //将time_interval加到date2
CONVERT_TZ (datetime2 ,fromTZ ,toTZ ) //转换时区
CURRENT_DATE ( ) //当前日期
CURRENT_TIME ( ) //当前时间
CURRENT_TIMESTAMP ( ) //当前时间戳
DATE (datetime ) //返回datetime的日期部分
DATE_ADD (date2 , INTERVAL d_value d_type ) //在date2中加上日期或时间
DATE_FORMAT (datetime ,FormatCodes ) //使用formatcodes格式显示datetime
DATE_SUB (date2 , INTERVAL d_value d_type ) //在date2上减去一个时间
DATEDIFF (date1 ,date2 ) //两个日期差
DAY (date) //返回日期的天
DAYNAME (date ) //英文星期
DAYOFWEEK (date ) //星期(1-7) ,1为星期天
DAYOFYEAR (date ) //一年中的第几天
EXTRACT (interval_name FROM date ) //从date中提取日期的指定部分
MAKEDATE (year ,day ) //给出年及年中的第几天,生成日期串
MAKETIME (hour ,minute ,second ) //生成时间串
MONTHNAME (date ) //英文月份名
NOW() //当前时间
SEC_TO_TIME (seconds ) //秒数转成时间
STR_TO_DATE (string ,format ) //字串转成时间,以format格式显示
TIMEDIFF (datetime1 ,datetime2 ) //两个时间差
TIME_TO_SEC (time ) //时间转秒数]
WEEK (date_time [,start_of_week ]) //第几周
YEAR (datetime ) //年份
DAYOFMONTH(datetime) //月的第几天
HOUR(datetime) //小时
LAST_DAY(date) //date的月的最后日期
MICROSECOND(datetime) //微秒
MONTH(datetime) //月
MINUTE(datetime) //分返回符号,正负或0
SQRT(number2) //开平方
create TABLE user (
id int(11) NOT NULL AUTO_INCREMENT,
userName varchar(50) DEFAULT NULL,
userAge int(11) DEFAULT NULL,
userAddress varchar(200) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
insert into user values(1, 'summer', 100, 'Shanghai Pudong');
/*
创建存储过程
in 参数例子
*/
DROP PROCEDURE IF EXISTS proc_user_demo_in; /*删除存储过程(如果存在)*/
CREATE PROCEDURE proc_user_demo_in(IN p_in INT)
BEGIN
SELECT p_in;
SET p_in=2;
SELECT p_in;
END
/*调用*/
/*定义客户端变量(与连接有关,当客户端退出时,变量会被释放)*/
SET @p_in=1;
CALL proc_user_demo_in(@p_in); /*1*/ /*2*/
SELECT @p_in; /*1*/
SHOW GLOBAL VARIABLES like '%';
/*p_in虽然在存储过程中被修改,但并不影响@p_id的值*/
/*
创建存储过程
out 例子
*/
drop procedure if exists proc_user_demo_out;
create procedure proc_user_demo_out(out p_out int)
BEGIN
select p_out;
SET p_out=2;
select p_out;
END;
SET @p_out=1;
call proc_user_demo_out(@p_out); /*null*/ /*2*/
SELECT @p_out; /*2*/
/*p_out在存储过程中被修改,影响@p_out的值*/
/*
创建存储过程
inout 例子
*/
drop procedure if exists proc_user_demo_inout;
create procedure proc_user_demo_inout(INOUT p_inout int)
BEGIN
select p_inout;
set p_inout=2;
select p_inout;
END;
SET @p_inout=1;
CALL proc_user_demo_inout(@p_inout); /*1*/ /*2*/
SELECT @p_inout; /*2*/
/*在存储过程中使用用户变量*/
drop procedure if exists proc_user_demo_user_variable;
create procedure proc_user_demo_user_variable()
BEGIN
select CONCAT(@greeting, ' world!') as 'greeting';
END;
set @greeting='hello';
call proc_user_demo_user_variable();
/*在存储间传递全局范围的用户变量*/
drop procedure if exists proc_user_demo_variable_transport1;
drop procedure if exists proc_user_demo_variable_transport2;
create procedure proc_user_demo_variable_transport1()
BEGIN
set @last_variable_transport='p1';
END;
create procedure proc_user_demo_variable_transport2()
BEGIN
select CONCAT('the last_variable_transport is ', @last_variable_transport) as 'last_variable_transport';
END;
CALL proc_user_demo_variable_transport1();
CALL proc_user_demo_variable_transport2();
/*
注意:
①用户变量名一般以@开头
②滥用用户变量会导致程序难以理解及管理
*/
/*在存数过程中使用内部变量*/
drop procedure if exists proc_user_demo_use_inner_variable;
create procedure proc_user_demo_use_inner_variable(IN param INTEGER)
BEGIN
DECLARE variable1 char(10) default 'nothing';
IF param=1 THEN
SET variable1='birds';
ELSEIF param=2 THEN
SET variable1='beasts';
END IF;
INSERT INTO user(userName) values(variable1);
END;
CALL proc_user_demo_use_inner_variable(3);
/*查看数据库中的表和存储过程*/
show tables; /*查看数据库的所有表*/
show create TABLE mybatis.user; /*查看某张表的创建详情*/
select * from mysql.proc where db='mybatis'; /*查看数据库存储过列表*/
show create procedure mybatis.proc_user_demo_in; /*查看某个存储过程的创建详情*/
/*
存储过程:变量作用域
内部的变量在其作用域范围内享有更高的优先权,当执行到end。变量时,内部变量消失,此时已经在其作用域外,变量不再可见了,应为在存储
过程外再也不能找到这个申明的变量,但是你可以通过out参数或者将其值指派
给会话变量来保存其值。
*/
drop procedure if exists proc_user_demo_variable_scope;
create procedure proc_user_demo_variable_scope()
BEGIN
declare x1 varchar(5) default 'outer';
BEGIN
declare x1 varchar(5) default 'inner';
select x1; /*inner*/
END;
select x1; /*outer*/
END;
call proc_user_demo_variable_scope();
/*存储过程:条件语句*/
drop procedure if exists proc_user_demo_if_then_else;
create procedure proc_user_demo_if_then_else(IN param INT)
BEGIN
declare var int default 0;
declare maxid int;
set var=param;
select max(id) into maxid from user;
IF var=0 THEN
insert into user(userName, userAge) values('nothing', var);
END IF;
IF var=1 THEN
update user set userAge=1 where id=maxid;
ELSE
update user set userAge=100 where id=maxid;
END IF;
END;
call proc_user_demo_if_then_else(0);
/*存储过程:case语句*/
drop procedure if exists proc_user_demo_case_when;
create procedure proc_user_demo_case_when(in param int)
BEGIN
declare var int;
set var=param;
CASE var
WHEN 0 THEN
insert into user(userName, userAge) values('nothing', 0);
WHEN 1 THEN
insert into user(userName, userAge) values('nothing', 1);
ELSE
insert into user(userName, userAge) values('nothing', 100);
END CASE;
END;
CALL proc_user_demo_case_when(15);
/*存储过程:循环语句 while*/
drop procedure if exists proc_user_demo_while;
create procedure proc_user_demo_while()
BEGIN
declare var int default 0;
while var<6 DO
insert into user(userName, userAge) values(CONCAT('name',var), var);
set var=var+1;
END WHILE;
END;
CALL proc_user_demo_while();
/*存储过程:循环语句 repeat*/
/*它在执行操作后检查结果,而while则是执行前进行检查*/
drop procedure if exists proc_user_demo_repeat;
create procedure proc_user_demo_repeat(in repeat_times int)
BEGIN
DECLARE var int default 1;
REPEAT
insert into user(userName, userAge) values(CONCAT('name',var), var);
set var=var+1;
UNTIL var>repeat_times END REPEAT;
END;
CALL proc_user_demo_repeat(3);
/*存储过程:循环语句 loop*/
drop procedure if exists proc_user_demo_loop;
create procedure proc_user_demo_loop(in loop_times int)
BEGIN
declare var int default 1;
LOOP_LABLE:loop
insert into user(userName, userAge) values(CONCAT('name',var), var);
set var=var+1;
IF var>loop_times THEN
LEAVE LOOP_LABLE;
END IF;
END LOOP;
END;
CALL proc_user_demo_loop(2);
存储过程是用户定义的一系列sql语句的集合,涉及特定表或其它对象的任务,用户可以调用存储过程,而函数通常是数据库已定义的方法,它接收参数并返回某种类型的值并且不涉及特定用户表。
存储过程和函数存在以下几个区别:
示例:
/*定义函数*/
DROP FUNCTION IF EXISTS fun_user_demo_sum;
CREATE FUNCTION fun_user_demo_sum(a INT(2),b INT(2)) RETURNS INT(4)
BEGIN
DECLARE sum_ INT(2) DEFAULT 0;
SET sum_ = a + b;
RETURN sum_;
END;
/*使用函数*/
select fun_user_demo_sum(1,2) as 'sum_';
/*定义存储过程*/
drop procedure if exists proc_user_demo_sum;
CREATE PROCEDURE proc_user_demo_sum(IN a INT(2), IN b INT(2), OUT sum INT(4))
BEGIN
SET sum=a+b;
END;
/*使用存储过程*/
call proc_user_demo_sum(1, 2, @c);
select @c;