MySQL存储过程详解

一、使用场景
1.执行时简单:名字+参数
2.存储过程中可以有一个、多个sql语句。
问:为啥要用存储过程?
答:
①将重复性很高的一些操作,封装到一个存储过程中。简化了对这些SQL的调用。
②批量处理sql+循环
③统一接口
注意:mysql中,存储功能相对较弱。使用较少。

二、基本建立语法
例1:

DELIMITER $$
CREATE PROCEDURE delete_matches(IN p playerno INTEGER)
    BEGIN
        DELETE FROM MATCHES
            WHERE playerno=p_playerno;
    END$$
DELIMITER;
call delete_matches(6);

例2:

delimiter $$
CREATE PROCEDURE dorepeat(p1 INT)
BEGIN
  SET @x = 0;  --》用set赋值, repeat循环
  REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT;
END$$
delimiter ;
CALL dorepeat(1000);
SELECT @x;
执行后:@x的值为1001

@变量名字的作用:

  • ①存储过程中定义,存储过程中进行处理,加减乘除。
  • ②存储过程外面可以访问这个@变量,这个变量保存了存储过程中对这个变量处理后的值。

@x:用户变量

  • 存储过程外面可以访问,一直被保存。
  • 直接使用set @x=0;就可以生成这么一个变量,不需要数据类型。

三、存储过程参数
三类:in(输入参数)、out(输出参数)、inout(输入和输出参数)
存储过程需要是活的–》输入不同的参数,执行不同的操作,处理不同的行等等。

IN参数

DELIMITER $$
CREATE PROCEDURE delete matches(IN p playerno INTEGER)
    BEGIN
        DELETE FROM MATCHES
            WHERE playerno=p_playerno;
    END$$
DELIMITER;
call delete_matches(6);

IN参数可以是:数值100、赋值了的变量 set @a=100;

OUT参数

delimiter $$
CREATE PROCEDURE simpleproc (OUT param1 INT)
    BEGIN
      SELECT COUNT(*) INTO param1 FROM PLAYERS;
    END$$
delimiter ;
CALL simpleproc(@a);
SELECT @a;

OUT参数:变量就可以,可以没有赋值。 @a

INOUT参数

delimiter $$
CREATE PROCEDURE simpleproc1(INOUT param1 INT)
BEGIN
  SELECT COUNT(*) INTO param1 FROM PLAYERS where PLAYERNO<=param1;
END$$
delimiter ;
set @a=10;
CALL simpleproc1(@a);    --》调用存储过程时,只要引用变量就行!
SELECT @a;
INOUT参数:只能是赋了值得变量。 set @a=100

一个存储过程如果想有返回值:

  • 1.out参数
  • 2.inout参数
  • 3.在存储过程内部定义用户变量

    @变量名字
    建议:返回值使用out参数;输入值使用in参数。

四、begin…end语法
为什么需要begin…end?
答:
①因为begin…end语句之间的n条语句属于一条语句,一个整体。
②成对出现。

BEGIN
     sql语句
END$$

五、变量
1.用户变量@

  • 随处可定义,随处可使用;
  • 不定义可直接使用;
  • 随处可见;
  • 内部定义,外部可见。

2.局部变量,只有名字,没有@符号

  • 先定义,再使用;
  • 只在存储过程内部可见;只能定义在begin后面!

注意:在存储过程内部,使用局部变量,不要使用用户变量。

用户变量和局部变量的区别:

  • ①局部变量前面没有@符号
  • ②当begin…end块处理完后,局部变量就消失了,而用户变量存在于整个会话之中。

定义局部变量:
DECLARE 变量名字 类型 [default] –》而且declare必须紧接着begin!
例:

DECLARE var_name [, var_name] ... type [DEFAULT value]
delimiter $$
CREATE PROCEDURE sp1 (x VARCHAR(10))
BEGIN
  DECLARE newname VARCHAR(10);
  DECLARE xid INT;
  SELECT xname, id INTO newname, xid
    FROM table1 WHERE xname = x;
  SELECT newname;
END$$
delimiter ;
call sp1('a');

初始化局部变量:

delimiter $$
CREATE  PROCEDURE test1(OUT num1 INTEGER)   
    BEGIN     
      DECLARE num2 INTEGER DEFAULT (SELECT count(*) FROM PLAYERS);
      SET num1 = num2;
    END$$
delimiter ;

begin…end的作用域:

  • 外层看不到内层;
  • 内层可以看到外层;
  • 平行互相看不见。

SET
可以给用户变量和局部变量赋值
用户变量内外都可以使用
局部变量,只有begin end之间可以使用,而且需要declare定义

六、IF…ELSE

DELIMITER $$    --》定义$$为分界符
CREATE  PROCEDURE difference(
  IN p1 INTEGER,        --》整数
  IN p2 INTEGER,  
  OUT p3 INTEGER)   
 BEGIN     
   IF p1 > p2 THEN
      SET p3 = 1;
   ELSEIF p1= p2 THEN
     SET p3 = 2;
   ELSE
     SET p3 = 3;
   END IF;        
 END$$
DELIMITER ;
mysql> call difference(1,10,@p);
    Query OK, 0 rows affected (0.00 sec)
mysql> select @p;
+------+
| @p   |
+------+
|    3 |
+------+
1 row in set (0.01 sec)

七、函数
1.返回值
两个return:一个标志返回什么类型;一个实际的返回值。
2.调用函数:函数需要出现在=的右边

例子:

DELIMITER $$
CREATE FUNCTION SimpleCompare(n INT, m INT)
  RETURNS VARCHAR(20)
  BEGIN
    DECLARE s VARCHAR(20);
    IF n > m THEN SET s = '>';
    ELSEIF n = m THEN SET s = '=';
    ELSE SET s = '<';
    END IF;
    SET s = CONCAT(n, ' ', s, ' ', m);
    RETURN s;
  END$$
DELIMITER ;
mysql> select SimpleCompare(1,2) ;
+--------------------+
| SimpleCompare(1,2) |
+--------------------+
| 1 < 2              |
+--------------------+
1 row in set (0.01 sec)
mysql> set @a=SimpleCompare(1,2) ;
Query OK, 0 rows affected (0.01 sec)
mysql> select @a;
+-------+
| @a    |
+-------+
| 1 < 2 |
+-------+
1 row in set (0.00 sec)

八、case…end case
例1:

DELIMITER $$
CREATE  PROCEDURE difference1(
  IN p1 INTEGER,
  IN p2 INTEGER,  
  OUT p3 INTEGER)   
 BEGIN 
   CASE    
     WHEN  p1 > p2 THEN
       SET p3 = 1;
     WHEN p1= p2 THEN
       SET p3 = 2;
     ELSE
       SET p3 = 3;
   END CASE;        
 END$$
DELIMITER ;
mysql> call difference1(1,100,@a);
Query OK, 0 rows affected (0.01 sec)
mysql> select @a;
+------+
| @a   |
+------+
|    3 |
+------+
1 row in set (0.00 sec)

例2:

DELIMITER $$
CREATE PROCEDURE p(x INT)
  BEGIN
    DECLARE v INT DEFAULT 1;
    set v=x;
    CASE v
      WHEN 2 THEN SELECT v;
      WHEN 3 THEN SELECT 0;
      ELSE
        BEGIN
        END;
    END CASE;
  END$$
DELIMITER ;
mysql> call p(2);
+------+
| v    |
+------+
|    2 |
+------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.01 sec)
mysql> call p(3);
+---+
| 0 |
+---+
| 0 |
+---+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)

九、while
例题:

DELIMITER $$
CREATE PROCEDURE dowhile(x int)
BEGIN
  DECLARE v1 INT DEFAULT 5;
    set v1=x;
  WHILE v1 > 0 DO
    select v1;
    SET v1 = v1 - 1;
  END WHILE;
END$$
DELIMITER ;
mysql> call dowhile(10);
+------+
| v1   |
+------+
|   10 |
+------+
1 row in set (0.01 sec)
+------+
| v1   |
+------+
|    9 |
+------+
1 row in set (0.01 sec)
。。。一直到1

十、repeat…until
先执行,再判断。
例题:

delimiter //
CREATE PROCEDURE dorepeat1(p1 INT)
BEGIN
  SET @x = 0;
  REPEAT
    SET @x = @x + 1;
  UNTIL @x > p1 
  END REPEAT;
END//
delimiter ;
mysql> call dorepeat1(10);
Query OK, 0 rows affected (0.01 sec)
mysql> select @x;
+------+
| @x   |
+------+
|   11 |
+------+

十一、loop配合leave
leave:跳出语句块

DELIMITER $$
CREATE  PROCEDURE small_exit(OUT p1 INTEGER,OUT p2 INTEGER)
   BEGIN
      SET p1 = 1;
      SET p2 = 1;
      block1: BEGIN 
         LEAVE block1;    --》就不再执行下面那个p2=3了!!
         SET p2 = 3;  
      END block1;  
      SET p1 = 4;    
   END$$
DELIMITER ;
mysql> call small_exit(@a,@b);
Query OK, 0 rows affected (0.00 sec)
mysql> select @a,@b;
+------+------+
| @a   | @b   |
+------+------+
|    4 |    1 |
+------+------+
1 row in set (0.00 sec)

loop

DELIMITER $$
CREATE  PROCEDURE wait_n(IN wait_seconds INTEGER)
   BEGIN                                                     ---》PSinterval 间隔
      DECLARE end_time datetime DEFAULT now() + INTERVAL wait_seconds SECOND; 
      wait_loop:LOOP
        IF now() > end_time THEN
           LEAVE wait_loop;
        END IF;   
      END LOOP wait_loop;      
   END$$
DELIMITER ;
mysql> call wait_n(10);
Query OK, 0 rows affected (10.55 sec)

while:先判断再执行
repeat…until:先执行再判断
loop:判断可以放在loop…end loop之间的任意位置
iterate:跳出本次循环

十二、ITERATE

DELIMITER $$
CREATE PROCEDURE doiterate3(p1 INT,p2 INT)
BEGIN
  label1: LOOP
    SET p1 = p1 + 1;
    SET p2 = p2 + 1;
    IF p1 < 10 THEN
        select p1;
      ITERATE label1;    --》跳出本次label1循环!继续下次label1循环。
       select p2;    --》一直没执行
    END IF;
    LEAVE label1;
  END LOOP label1;
  SET @x = p1;
END$$
DELIMITER ;
mysql> call doiterate3(10,10);    --》直接就执行leave label1
Query OK, 0 rows affected (0.01 sec)
mysql> call doiterate3(1,1);    ---》显示p1=2,3,4...9
+------+
| p1   |
+------+
|    2 |
+------+
1 row in set (0.00 sec)
+------+
| p1   |
+------+
|    3 |
+------+
1 row in set (0.00 sec)

十三、select info经典用法

DELIMITER $$
CREATE  PROCEDURE total_penalties_player(
  IN p_playerno INTEGER,
  OUT total_penalties DECIMAL(8,2))   
 BEGIN     
   SELECT sum(amount)        ---》去掉into的select只能返回一行数据!
     INTO total_penalties  --》把select 的结果into给(多个)变量!!
     FROM PENALTIES
     WHERE playerno = p_playerno;            
 END$$
DELIMITER ;
mysql> call total_penalties_player(44,@a);  ---》计算44号人的罚款总额,结果赋给@a
Query OK, 1 row affected (0.01 sec)
mysql> select @a;
+--------+
| @a     |
+--------+
| 130.00 |
+--------+
1 row in set (0.00 sec)

小结:

  • 1.经典的方式:将select返回的多列单行数据赋值给相应的变量(一个列对应一个变量)
  • 2.这些变量经常是out参数
  • 3.也就是变相的将select的结果传给了存储过程,让外面的程序可见。

例2:into给多个参数:

DELIMITER $$
CREATE  PROCEDURE get_address(
  IN p_playerno SMALLINT,
  OUT p_street VARCHAR(30),
  OUT p_houseno VARCHAR(4),
  OUT p_town VARCHAR(30),
  OUT p_postcode VARCHAR(6))   
 BEGIN     
   SELECT street, houseno, town, postcode
     INTO p_street, p_houseno, p_town, p_postcode
     FROM PLAYERS
     WHERE playerno = p_playerno;            
 END$$
DELIMITER ;
mysql> call get_address(44,@a,@b,@c,@d);
Query OK, 1 row affected (0.01 sec)
mysql> select @a,@b,@c,@d;
+--------------+------+-----------+--------+
| @a           | @b   | @c        | @d     |
+--------------+------+-----------+--------+
| Lewis Street | 23   | Inglewood | 4444LJ |
+--------------+------+-----------+--------+
1 row in set (0.00 sec)
posted @ 2017-11-08 21:02  斯言甚善  阅读(209)  评论(0编辑  收藏  举报