mysql存储过程详解

我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。

一个存储过程是一个可编程的函数,它在数据库中创建并保存。它可以有SQL语句和一些特殊的控制结构组成。当希望在不同的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是非常有用的。数据库中的存储过程可以看做是对编程中面向对象方法的模拟。它允许控制数据的访问方式。

存储过程是数据库存储的一个重要的功能,但是MySQL在5.0以前并不支持存储过程,这使得MySQL在应用上大打折扣。好在MySQL 5.0终于开始已经支持存储过程,这样即可以大大提高数据库的处理速度,同时也可以提高数据库编程的灵活性。

存储过程格式举例:

DELIMITER //          /* 分割符 */

DROP PROCEDURE IF EXISTS test //  /* 若存在去除重复*/

CREATE PROCEDURE test()  /*创造存储过程*/

BEGIN  /*执行开始*/

    DECLARE done INT DEFAULT 0;    /*定义内部变量*/

    DECLARE a VARCHAR(200) DEFAULT '';

    DECLARE c VARCHAR(200) DEFAULT '';

    DECLARE mycursor CURSOR FOR SELECT  t_crt_tm FROM demo_dep;  /*定义游标*/

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;      /*定义游标结束*/

    OPEN mycursor;  /*打开游标*/

    REPEAT   /*迭代处理游标*/

        FETCH mycursor INTO a;  /*取出游标数据*/

        IF NOT done THEN    /*判断是否有数据*/

            SET c=CONCAT(c,a);/*字符串相加*/

        END IF;

    UNTIL done END REPEAT;         /*无数据不再迭代*/

    CLOSE mycursor;  /*关闭游标*/

    SELECT c;  

END //  /*执行结束*/

DELIMITER ;  /*分隔符*/

CALL test();  /*执行存储过程*/

/*存储过程简单的可以只是几条sql语句的组合,代表组合执行,有游标为稍微复杂情况*/

注:

(1)这里需要注意的是DELIMITER //和DELIMITER ;两句,DELIMITER是分割符的意思,因为MySQL默认以";"为分隔符,如果我们没有声明分割符,那么编译器会把存储过程当成SQL语句进行处理,则存储过程的编译过程会报错,所以要事先用DELIMITER关键字申明当前段分隔符,这样MySQL才会将";"当做存储过程中的代码,不会执行这些代码,用完了之后要把分隔符还原。

(2)存储过程根据需要可能会有输入、输出、输入输出参数,这里有一个输出参数s,类型是int型,如果有多个参数用","分割开。

(3)过程体的开始与结束使用BEGIN与END进行标识。

(3). 参数

MySQL存储过程的参数用在存储过程的定义,共有三种参数类型,IN,OUT,INOUT,形式如:

CREATE PROCEDURE([[IN |OUT |INOUT ] 参数名 数据类形...])

IN 输入参数:表示该参数的值必须在调用存储过程时指定,在存储过程中修改该参数的值不能被返回,为默认值

OUT 输出参数:该值可在存储过程内部被改变,并可返回

INOUT 输入输出参数:调用时指定,并且可被改变和返回

Ⅰ. IN参数例子

创建:

mysql > DELIMITER //  

mysql > CREATE PROCEDURE demo_in_parameter(IN p_in int)  

-> BEGIN   

-> SELECT p_in;   

-> SET p_in=2;   

-> SELECT p_in;   

-> END;   

-> //  

mysql > DELIMITER ; 
执行结果:

mysql > SET @p_in=1;  

mysql > CALL demo_in_parameter(@p_in);  

+------+  

| p_in |  

+------+  

|   1  |   

+------+  

 

+------+  

| p_in |  

+------+  

|   2  |   

+------+  

 mysql> SELECT @p_in;  

+-------+  

| @p_in |  

+-------+  

|  1    |  

+-------+  
以上可以看出,p_in虽然在存储过程中被修改,但并不影响@p_id的值 

Ⅱ.OUT参数例子

创建:

mysql > DELIMITER //  

mysql > CREATE PROCEDURE demo_out_parameter(OUT p_out int)  

-> BEGIN 

-> SELECT p_out;  

-> SET p_out=2;  

-> SELECT p_out;  

-> END;  

-> //  

mysql > DELIMITER ; 
执行结果:

mysql > SET @p_out=1;  

mysql > CALL sp_demo_out_parameter(@p_out);  

+-------+  

| p_out |   

+-------+  

| NULL  |   

+-------+  

 

+-------+  

| p_out |  

+-------+  

|   2   |   

+-------+   

mysql> SELECT @p_out;  

+-------+  

| p_out |  

+-------+  

|   2   |  

+-------+  
Ⅲ. INOUT参数例子

创建:

mysql > DELIMITER //   

mysql > CREATE PROCEDURE demo_inout_parameter(INOUT p_inout int)   

-> BEGIN 

-> SELECT p_inout;  

-> SET p_inout=2;  

-> SELECT p_inout;   

-> END;  

-> //   

mysql > DELIMITER ; 

执行结果:

mysql > SET @p_inout=1;  

mysql > CALL demo_inout_parameter(@p_inout) ;  

+---------+  

| p_inout |  

+---------+  

|    1    |  

+---------+  

 

+---------+  

| p_inout |   

+---------+  

|    2    |  

+---------+  

 mysql > SELECT @p_inout;  

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

| @p_inout |   

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

|    2     |  

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

(4). 变量

Ⅰ. 变量定义

DECLARE variable_name [,variable_name...] datatype [DEFAULT value];

其中,datatype为MySQL的数据类型,如:int, float, date, varchar(length)

Ⅱ. 变量赋值

 SET 变量名 表达式值 [,variable_name = expression ...]

Ⅲ. 用户变量

ⅰ. MySQL客户端使用用户变量

mysql > SELECT 'Hello World' into @x;  

mysql > SELECT @x;  

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

|   @x        |  

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

HelloWorld |  

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

注意:

①用户变量名一般以@开头

②滥用用户变量会导致程序难以理解及管理

5.      MySQL存储过程的查询

我们像知道一个数据库下面有那些表,我们一般采用show tables;进行查看。那么我们要查看某个数据库下面的存储过程,是否也可以采用呢?答案是,我们可以查看某个数据库下面的存储过程,但是是令一钟方式。

我们可以用

select name from mysql.proc where db=’数据库名’;

或者

select routine_name from information_schema.routines where routine_schema='数据库名';

或者

show procedure status where db='数据库名';

进行查询。

如果我们想知道,某个存储过程的详细,那我们又该怎么做呢?是不是也可以像操作表一样用describe 表名进行查看呢?

答案是:我们可以查看存储过程的详细,但是需要用另一种方法:

SHOW CREATE PROCEDURE 数据库.存储过程名;

就可以查看当前存储过程的详细。

8.      MySQL存储过程的控制语句

(1). 变量作用域

内部的变量在其作用域范围内享有更高的优先权,当执行到end。变量时,内部变量消失,此时已经在其作用域外,变量不再可见了,应为在存储
过程外再也不能找到这个申明的变量,但是你可以通过out参数或者将其值指派
给会话变量来保存其值。

mysql > DELIMITER //  

mysql > CREATE PROCEDURE proc3()  

     -> begin 

     -> declare x1 varchar(5) default 'outer';  

     -> begin 

     -> declare x1 varchar(5) default 'inner';  

     -> select x1;  

     -> end;  

     -> select x1;  

     -> end;  

     -> //  

mysql > DELIMITER ;  

  (2). 条件语句

Ⅰ. if-then -else语句 

mysql > DELIMITER //  

mysql > CREATE PROCEDURE proc2(IN parameter int)  

     -> begin 

     -> declare var int;  

     -> set var=parameter+1;  

     -> if var=0 then 

     -> insert into t values(17);  

     -> end if;  

     -> if parameter=0 then 

     -> update t set s1=s1+1;  

     -> else 

     -> update t set s1=s1+2;  

     -> end if;  

     -> end;  

     -> //  

mysql > DELIMITER ;  
Ⅱ. case语句: 

mysql > DELIMITER //  

mysql > CREATE PROCEDURE proc3 (in parameter int)  

     -> begin 

     -> declare var int;  

     -> set var=parameter+1;  

     -> case var  

     -> 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;  

     -> //  

mysql > DELIMITER ; 

 (3). 循环语句

Ⅰ. while ···· end while:

mysql > DELIMITER //  

mysql > CREATE PROCEDURE proc4()  

     -> begin 

     -> declare var int;  

     -> set var=0;  

     -> while var<6 do  

     -> insert into t values(var);  

     -> set var=var+1;  

     -> end while;  

     -> end;  

     -> //  

mysql > DELIMITER ; 

Ⅱ. repeat···· end repeat:

它在执行操作后检查结果,而while则是执行前进行检查。

mysql > DELIMITER //  

mysql > CREATE PROCEDURE proc5 ()  

     -> begin   

     -> declare v int;  

     -> set v=0;  

     -> repeat  

     -> insert into t values(v);  

     -> set v=v+1;  

     -> until v>=5  

     -> end repeat;  

     -> end;  

     -> //  

mysql > DELIMITER ;  

 


Ⅲ. loop ·····end loop:

loop循环不需要初始条件,这点和while 循环相似,同时和repeat循环一样不需要结束条件, leave语句的意义是离开循环。

mysql > DELIMITER //  

mysql > CREATE PROCEDURE proc6 ()  

     -> begin 

     -> declare v int;  

     -> set v=0;  

     -> LOOP_LABLE:loop  

     -> insert into t values(v);  

     -> set v=v+1;  

     -> if v >=5 then 

     -> leave LOOP_LABLE;  

     -> end if;  

     -> end loop;  

     -> end;  

     -> //  

mysql > DELIMITER ;  

Ⅳ. LABLES 标号:

标号可以用在begin repeat while 或者loop 语句前,语句标号只能在合法的语句前面使用。可以跳出循环,使运行指令达到复合语句的最后一步。

(4). ITERATE迭代

Ⅰ. ITERATE:

通过引用复合语句的标号,来从新开始复合语句

mysql > DELIMITER //  

mysql > CREATE PROCEDURE proc10 ()  

     -> begin 

     -> declare v int;  

     -> set v=0;  

     -> LOOP_LABLE:loop  

     -> if v=3 then   

     -> set v=v+1;  

     -> ITERATE LOOP_LABLE;  

     -> end if;  

     -> insert into t values(v);  

     -> set v=v+1;  

     -> if v>=5 then 

     -> leave LOOP_LABLE;  

     -> end if;  

     -> end loop;  

     -> end;  

     -> //  

mysql > DELIMITER ;  

9.      MySQL存储过程的基本函数 

(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个字符,
注: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)  

TRIM([[BOTH|LEADING|TRAILING] [padding] FROM]string2) //去除指定位置的指定字符
UCASE (string2 ) //转换成大写
RIGHT(string2,length) //取string2最后length个字符
SPACE(count) //生成count个空格

(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为小数位数]

注:返回类型并非均为整数,如:
(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) 

SIGN (number2 ) //

(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) //开平方

九、mysql游标示例mysql游标简易教程

(一),认识游标(cursor)
就是一个可读的标识,用来标识数据取到什么地方了。
(二),游标特性
1,只读
2,不滚动
3,不敏感的
(三),使用游标
需要强调的是,游标必须在定义处理程序之前被定义,但变量必须在定义游标之前被定义,顺序就是变量定义-游标定义-处理程序。
1.定义游标
DECLARE cursor_name CURSOR FOR select_statement
这个语句声明一个游标。也可以在子程序中定义多个游标,一个块中的每一个游标必须命名唯一。声明游标后也是单条操作的。
2. 游标OPEN
OPEN cursor_name
这个语句打开先前声明的游标。
3. 游标FETCH
FETCH cursor_name INTO var_name [, var_name] ...
这个语句用指定的打开游标读取下一行(如果有下一行的话),并且前进游标指针至该行。
4. 游标CLOSE
CLOSE cursor_name
这个语句关闭先前打开的游标,注意,用完后必须关闭。
(四)示例,下面是一个存储过程,里面用到游标,逐条更新数据(批量更新数据)

BEGIN

 DECLARE  no_more_record INT DEFAULT 0;

 DECLARE  pID BIGINT(20);

 DECLARE  pValue DECIMAL(15,5);

 DECLARE  cur_record CURSOR FOR   SELECT colA, colB from tableABC;  /*首先这里对游标进行定义*/

 DECLARE  CONTINUE HANDLER FOR NOT FOUND  SET  no_more_record = 1; /*这个是个条件处理,针对NOT FOUND的条件,当没有记录时赋值为1*/

 OPEN  cur_record; /*接着使用OPEN打开游标*/

 FETCH  cur_record INTO pID, pValue; /*把第一行数据写入变量中,游标也随之指向了记录的第一行*/

 WHILE no_more_record != 1 DO

 INSERT  INTO testTable(ID, Value)

 VALUES  (pID, pValue);

 FETCH  cur_record INTO pID, pValue;

 END WHILE;

 CLOSE  cur_record;  /*用完后记得用CLOSE把资源释放掉*/

END

十、实例

最后分享一个我写的较为复杂的存储过程的案例,(感谢dbForge Studio for MySQL工具,使得本人写的存储过程可以调试成功,由于时间仓促作为案例的存储过程还有点问题,每奇数次执行会报错,偶数次执行则没有问题,我没有再去debug十分抱歉

全数据库单信息查询:

USE db_my_test_;

/*首先创造个表去存储查询出的信息,可定义临时表*/

CREATE TABLE zzzz_result (

  id int(11) NOT NULL AUTO_INCREMENT,

  tab varchar(255) DEFAULT NULL,

  col varchar(255) DEFAULT NULL,

  PRIMARY KEY (id)

)

ENGINE = INNODB

AUTO_INCREMENT = 13

AVG_ROW_LENGTH = 16384

CHARACTER SET utf8

COLLATE utf8_general_ci;

DELIMITER $$

/*子存储过程1:根据过程中生成视图的结果数,输出判定结果,若有数据为1,无为0*/

CREATE DEFINER = 'root'@'localhost'

PROCEDURE mycount2 (OUT b int)

BEGIN

  DECLARE done int DEFAULT 0;

  DECLARE a int DEFAULT 0;

  DECLARE mycursor CURSOR FOR

  SELECT

    COUNT(*)

  FROM tree_test_view;

  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

  OPEN mycursor;

  REPEAT

    FETCH mycursor INTO a;

    IF NOT done THEN

      SET b = 0;

      IF a > 0 THEN

        SET b = 1;

      END IF;

    END IF;

  UNTIL done END REPEAT;

  CLOSE mycursor;

END

/*主存储过程,根据输入信息,判定'db_my_test_'数据库中哪个表,哪个字段存在这个数据。读者可根据需要修改数据库名称*/

CREATE DEFINER = 'root'@'localhost'

PROCEDURE myproce (IN z varchar(200))

BEGIN

  DECLARE a varchar(200) DEFAULT '';

  DECLARE b varchar(200) DEFAULT '';

  DECLARE c varchar(200) DEFAULT '';

  DECLARE d int DEFAULT 0;

  DECLARE done int DEFAULT 0;

  DECLARE mycursor CURSOR FOR

  SELECT

    table_name,

    column_name

  FROM information_schema.columns

  WHERE table_schema = 'db_my_test_'  /*数据库名*/

  ORDER BY table_schema, table_name;

  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

  OPEN mycursor;

  REPEAT

    FETCH mycursor INTO a, b;

    IF NOT done THEN

      SET c = CONCAT("select * from ", a, " where ", b, "='", z, "'");

      CALL proc1(c);

      CALL mycount2(d);

      IF d = 1 THEN

        CALL myresult(a, b);

      END IF;

    END IF;

  UNTIL done END REPEAT;

  CLOSE mycursor;

END

/*子存储过程2:将符合要求的表名列名存入结果表中*/

CREATE DEFINER = 'root'@'localhost'

PROCEDURE myresult (IN a varchar(200), IN b varchar(200))

BEGIN

  SET @sqlstr = "INSERT INTO zzzz_result(tab,col)  VALUES ('";

  SET @sqlstr = CONCAT(@sqlstr, a, "','", b, "');");

  PREPARE stmt FROM @sqlstr;     /*定义*/

  EXECUTE stmt; /*执行预处理语句sql*/

  DEALLOCATE PREPARE stmt; /*删除定义*/

END

/*子存储过程3:根据主过程生成的sql语句执行生成视图,以便后续处理*/

CREATE DEFINER = 'root'@'localhost'

PROCEDURE proc1 (IN top varchar(200))

BEGIN

  DROP VIEW IF EXISTS tree_test_view;

  SET @sqlstr = "CREATE or replace VIEW tree_test_view as ";

  SET @sqlstr = CONCAT(@sqlstr, top);

  PREPARE stmt FROM @sqlstr;

  EXECUTE stmt;

  DEALLOCATE PREPARE stmt;

END

DELIMITER ;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值