MySQL-快速入門(8)存儲過程、存儲函數


1、存儲過程

  1》創建存儲過程:create procedure

create procedure sp_name ([in | out | inout] param_name type)
[characteristics ...]  routine_body

    characteristics指定存儲過程的特性:

      1>language sql:說明routine_body部分是由sql語句組成的,當前系統支持的語言為sql,sql是language特性的唯一值。

      2>[not] deterministic:指明存儲過程執行的結果是否確定。deterministic表示結果是確定的。每次執行存儲過程時,相同的輸入會得到相同的輸出。not deterministic表示結果是不確定的,相同的輸入可能得到不同的輸出。如果沒有指定任意一個值,默認為not deterministic。

      3>{contains sql | no sql | reads sql data | modifies sql data}:指明子程序使用sql語句的限制。contains sql表明子程序包含sql語句,但是不包含讀寫數據的語句;no sql表明子程序不包含sql語句;reads sql data說明子程序包含讀數據的語句;modifies sql data表明子程序包含寫數據的語句。默認情況下,系統會指定為contains sql。

      4>sql security{definer | invoker}:指明誰有權限來執行。definer表示只有定義者才能執行。invoker表示擁有權限的調用者可以執行。默認情況下,系統指定為definer。

      5>comment 'string':注釋信息,可以用來描述存儲過程或函數。

    routine_body:sql代碼的內容,可以用begin...end;來表示sql代碼的開始和結束。

//最簡單的存儲過程
create
procedure p_get_avg() begin ... end;
//"delimiter //"的作用是將MySQL的結束符設置為//,因為MySQL默認的語句結束符號為分號";",
//為了避免與存儲過程中sql語句的結束符相沖突,需要使用delimiter改變存儲過程的結束符,並
//"end//"結束存儲過程。"delimiter ;"則是恢復默認的";"結束符。
mysql> delimiter // mysql> create procedure p_get_avg() -> begin -> select * from test; -> end// Query OK, 0 rows affected (0.02 sec) mysql> delimiter ;

2、存儲函數

  1》創建存儲函數:create function

//指定in、out、inout只對procedure是合法的,在function中總是默認為in類型
//如果return返回的類型不同於returns指定的類型,返回值將會被強制轉換為恰當的類型。

create
function func_name([in | out | inout] param_name type) returns type [characteristic ...] routine_body
mysql> delimiter //
mysql> create function f_get_avg()
    -> returns char(25)
    -> begin
    ->   return (select name from test where id=1);
    -> end//
Query OK, 0 rows affected (0.00 sec)

mysql> delimiter ;
mysql> delimiter //
mysql> drop function if exists f_get_avg;
    -> CREATE FUNCTION f_get_avg()
    -> RETURNS int
    -> begin
    ->   declare i int default 0;
    ->   select 666 into i from dual;
    ->   return i;
    -> end//
Query OK, 0 rows affected (0.02 sec)

Query OK, 0 rows affected (0.02 sec)

mysql> delimiter ;
mysql> select f_get_avg();
+-------------+
| f_get_avg() |
+-------------+
|         666 |
+-------------+
1 row in set (0.00 sec)

mysql>
注:trigger和function都需要寫成 select ...into  這種句式,否者會報1415錯誤?
CREATE
DEFINER=`root`@`localhost` FUNCTION `f_get_avg`() RETURNS int(11) begin declare i int default 0; /*select 666 into i from dual;*/ return i; end
CREATE DEFINER=`root`@`localhost` FUNCTION `f_get_avg`() RETURNS int(11)
begin
  declare i int default 0; 
  /*select 666 into i from dual;*/
  set i=999;
  return i;
end

3、變量的使用

   1》在存儲過程中使用declare語句定義變量:

declare var_name [,var_name]... data_type [default value];
即:
declare var1,var2,var3 int;

   2》為變量賦值:定義變量后,MySQL使用set語句為變量賦值

set var_name = expr [,var_name=expr] ...;
或者
select col_name[,...] into var_name[,...] tb_expr;
即:
set var1=10,var2=20;

4、定義條件和處理程序:都是使用declare關鍵字

  1》定義條件

//codition_name:表示條件的名稱;condition_type參數表示條件的類型;
//sqlstste_value和mysql_error_code:都可以表示MySQL的錯誤
//sqlstate_value:為長度為5的字符串類型錯誤代碼。
//mysql_error_code:為數值類型錯誤代碼。
//例如:error 1142(42000)中,sqlstate_value的值是42000,mysql_error_code的值為1142。
declare
condition_name condition for [condition_type] 其中[condition_type]:sqlstate [value] sqlstate_value
| mysql_error_code
即:
declare condition_name condition for sqlstate [value] sqlstate_value
或者
declare condition_name condition for mysql_error_code

舉例:

//定義"error 1148(42000)"錯誤,名稱為command_not_allowed
//方法一:使用sqlstate_value declare command_not_allowed condition for sqlstate '42000';
//方法二:使用mysql_error_code declare command_not_allowed condition for 1148;

  2》定義處理程序

declare handler_type handler for condition_value[,...] sp_statement 

sp_statement:程序語句段,表示遇到定義的錯誤時,需要執行的存儲過程或函數

//錯誤處理方式
//continue:表示遇到錯誤不處理。繼續執行;
//exit:遇到錯誤馬上退出;
//undo:表示遇到錯誤后撤回之前的操作,MySQL暫時不支持這樣的操作 handler_type:
continue | exit | undo
//錯誤類型
//sqlstate [value] sqlstate_value:包含5個字符的字符串錯誤值
//condition_name:表示declare condition定義的錯誤條件名稱
//mysql_error_code:匹配數值類型錯誤代碼

//sqlwarning:匹配所有以01開頭的sqlstate錯誤代碼
//not found:匹配所有以02開頭的sqlstate錯誤代碼
//sqlexception:匹配所有沒有被sqlwarning或not found捕獲的sqlstate錯誤代碼
condition_value: sqlstate
[value] sqlstate_value | condition_name | mysql_error_code | not found | sqlexception | sqlwarning

舉例:

//方法一:捕獲sqlstate_value 
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' SET @info='NO_SUCH_TABLE';

//方法二:捕獲mysql_error_code
DECLARE CONTINUE HANDLER FOR 1146 SET @info=' NO_SUCH_TABLE ';

//方法三:先定義條件,然后調用
DECLARE  no_such_table  CONDITION  FOR  1146;
DECLARE CONTINUE HANDLER FOR NO_SUCH_TABLE SET @info=' NO_SUCH_TABLE ';

//方法四:使用SQLWARNING
DECLARE EXIT HANDLER FOR SQLWARNING SET @info='ERROR';

//方法五:使用NOT FOUND
DECLARE EXIT HANDLER FOR NOT FOUND SET @info=' NO_SUCH_TABLE ';

//方法六:使用SQLEXCEPTION
DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @info='ERROR'; 
mysql> DELIMITER //
mysql> CREATE PROCEDURE handlerdemo ()
    ->       BEGIN
    ->        DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
    ->        SET @x = 1;
    ->        INSERT INTO t VALUES (1);
    ->        SET @x = 2;
    ->        INSERT INTO t VALUES (1);
    ->        SET @x = 3;
    ->       END;
    ->      //
Query OK, 0 rows affected (0.02 sec)

mysql> DELIMITER ;
mysql> CALL handlerdemo();
Query OK, 0 rows affected (0.02 sec)

mysql> select @x;
+------+
| @x   |
+------+
|    3 |
+------+
1 row in set (0.00 sec)

mysql> select @x2;
+------+
| @x2  |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> select * from t;
+----+
| s1 |
+----+
|  1 |
+----+
1 row in set (0.00 sec)

mysql>

   @x是一個用戶變量,執行結果@x等於3,說明存儲過程被執行到了最后面一句。如果省去異常處理那一句,第2個insert因為主鍵約束強制失敗之后,存儲過程可能已經采取默認(exit)路徑,此時select @x返回的結果可能是2。

"@var_name"表示用戶變量,使用set語句為其賦值,用戶變量與連接有關,一個客戶端定義的變量不能被其他客戶端看到或使用。
當客戶端連接退出時,該客戶端連接的所有變量將自動釋放。

5、光標的使用:MySQL中的光標只能在存儲過程、存儲函數中使用

  1》聲明光標

declare cursor_name cursor for select_statement;

  2》打開關標

open cursor_name;

  3》使用光標

fetch cursor_name into var_name[,var_name] ...;

  4》關閉光標

close cursor_name;

  舉個例子:

create procedure p3()
begin
    declare id int;
    declare name varchar(15);
    declare flag int default 0;
    -- 聲明游標
    declare mc cursor for select * from class1;
    declare continue handler for not found set flag = 1;
    -- 打開游標
    open mc;
    -- 獲取結果
    loop_label_0:loop 
      fetch mc into id,name;
      if flag=1 then -- 當無法fetch會觸發handler continue
           leave loop_label_0;
      end if;
      -- 這里是為了顯示獲取結果
      insert into class2 values(id,name);
      -- 關閉游標
    end loop;
close mc; end; call p3();-- 不報錯 select * from class2;

6、流程控制使用

  MySQL中用於流程控制的語句有:if、case、loop、leave、iterate、repeat、while語句。每個流程可能包含一個單獨語句,或者使用begin...end構造的符合語句,構造可以被嵌套。

  1》if語句

//statement_list可以包含一個或者多個語句
if expr_condition then 
statement_list
[elseif expr_condition then statement_list ...] ... [else statement_list] end if

  2》case語句。與“控制流程函數”中的case是不同的。

//第一種方式:
//匹配表達式的值
case case_expr
  when  when_value  then  statement_list
  [when  when_value  then statement_list] ...
  [else statement_list]   //不能有else null子句
end case

//第二種方式:
//逐個表達式執行,直到有一個表達式為true被執行
case
  when expr_condition then statement_list
  [when expr_condition then statement_list] ...
  [else statement_list]   //不能有else null子句
end case

  3》loop語句

[loop_label:] loop
   statement_list
//如果要跳出循環,這時候label是必須的
if expr_condition then leave loop_label
end if;
end loop [loop_label]

   4》leave語句

//用來退出任何被標注的流程控制構造,包括begin...end和循環體。
leave label

   5》iterate語句

//將執行順序轉到語句段開頭處
//iterate只可以出現在loop、repeat、while語句內,iterate表示再次循環,label參數表示循環的標志。
//iterate語句必須跟在循環標志前面。 iterate label
CREATE PROCEDURE doiterate()
BEGIN
DECLARE p1 INT DEFAULT 0;
my_loop: LOOP
  SET p1= p1 + 1;
  IF p1 < 10 THEN ITERATE my_loop;
  ELSEIF p1 > 20 THEN LEAVE my_loop;
  END IF;
  SELECT 'p1 is between 10 and 20';
END LOOP my_loop;
END

   6》repeat語句

      創建一個帶條件判斷的循環過程,每次語句執行完畢,會對條件表達式進行判斷,如果表達式為真,則循環結束;否則重復執行循環中的語句。相當於do...while語句。

[repeat_label:]repeat
  statement_list
until expr_condition
end repeat [repeat_label]

    7》while語句

[while_label:]while expr_condition do
   statement_list
end while [while_label]

7、調用存儲過程、存儲函數

  存儲過程的調用必須使用call語句,並且存儲過程和數據庫相關,如果要執行其他數據庫中的存儲過程,需要指定數據庫名稱。

  存儲函數的調用與MySQL中定義的函數的調用方式相同。

  1》調用存儲過程

call sp_name([parameter[,...]])

  CREATE DEFINER=`root`@`localhost` PROCEDURE `p_get_name`(in i_id int,out o_name varchar(25))
  begin
    select name into o_name from test t where t.id=i_id;
  end

-----------------------------------
mysql> call p_get_name(1,@name); Query OK, 1 row affected (0.00 sec) mysql> select @name; +-------+ | @name | +-------+ | Lucy | +-------+ 1 row in set (0.00 sec) mysql>

   2》調用存儲函數:與MySQL內部定義的函數一樣的使用方法。

CREATE DEFINER=`root`@`localhost` FUNCTION `f_get_avg`() RETURNS char(50) CHARSET utf8
begin
  return (select avg(salary) from test);
end

--------------------------------------
mysql> select f_get_avg();
+-------------+
| f_get_avg() |
+-------------+
| 1180        |
+-------------+
1 row in set (0.00 sec)

mysql>

8、查看存儲過程和函數,方法有3種:

  1》show status

//[like 'pattern']指匹配存儲過程或函數的名稱
show {procedure | function} status [like 'pattern']
mysql> show procedure status like 'p_get_name%' \G
*************************** 1. row ***************************
                  Db: mybatis
                Name: p_get_name
                Type: PROCEDURE
             Definer: root@localhost
            Modified: 2019-09-05 00:28:04
             Created: 2019-09-05 00:28:04
       Security_type: DEFINER
             Comment:
character_set_client: utf8
collation_connection: utf8_general_ci
  Database Collation: utf8_general_ci
1 row in set (0.00 sec)

mysql>

  2》show create

show createprocedure | function} sp_name
mysql> show create function f_get_avg \G
*************************** 1. row ***************************
            Function: f_get_avg
            sql_mode: STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITU
TION
     Create Function: CREATE DEFINER=`root`@`localhost` FUNCTION `f_get_avg`() R
ETURNS char(50) CHARSET utf8
begin
  return (select avg(salary) from test);
end
character_set_client: utf8
collation_connection: utf8_general_ci
  Database Collation: utf8_general_ci
1 row in set (0.00 sec)

mysql>

  3》從系統的information_schema數據庫查詢

    MySQL中存儲過程和存儲函數的信息存儲在information_schema數據庫下的Routines表中。

select * from information_schema.Routines
where routine_name=' sp_name ' and routine_type='procedure | function';
mysql> select * from information_schema.routines where routine_name='p_get_name'
 and routine_type='procedure' \G
*************************** 1. row ***************************
           SPECIFIC_NAME: p_get_name
         ROUTINE_CATALOG: def
          ROUTINE_SCHEMA: mybatis
            ROUTINE_NAME: p_get_name
            ROUTINE_TYPE: PROCEDURE
               DATA_TYPE:
CHARACTER_MAXIMUM_LENGTH: NULL
  CHARACTER_OCTET_LENGTH: NULL
       NUMERIC_PRECISION: NULL
           NUMERIC_SCALE: NULL
      DATETIME_PRECISION: NULL
      CHARACTER_SET_NAME: NULL
          COLLATION_NAME: NULL
          DTD_IDENTIFIER: NULL
            ROUTINE_BODY: SQL
      ROUTINE_DEFINITION: begin
  select name into o_name from test t where t.id=i_id;
end
           EXTERNAL_NAME: NULL
       EXTERNAL_LANGUAGE: NULL
         PARAMETER_STYLE: SQL
        IS_DETERMINISTIC: NO
         SQL_DATA_ACCESS: CONTAINS SQL
                SQL_PATH: NULL
           SECURITY_TYPE: DEFINER
                 CREATED: 2019-09-05 00:28:04
            LAST_ALTERED: 2019-09-05 00:28:04
                SQL_MODE: STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBS
TITUTION
         ROUTINE_COMMENT:
                 DEFINER: root@localhost
    CHARACTER_SET_CLIENT: utf8
    COLLATION_CONNECTION: utf8_general_ci
      DATABASE_COLLATION: utf8_general_ci
1 row in set (0.00 sec)

mysql>

9、修改存儲過程、函數(並不是修改其中的代碼)

alter {procedure | function} sp_name [characteristic ...]

characteristic指定存儲過程的特性,可能的取值有:
  contains sql:表示子程序包含sql語句,但不包含讀或寫數據的語句。
no sql:表示子程序中不包含sql語句。
reads sql data:表示子程序中包含讀數據的語句。
modifies sql data:表示子程序中包含寫數據的語句。
sql security {definer | invoker}:指明誰有權限來執行。
definer:表示只有定義者自己才能夠執行。
invoker:表示調用者可以執行。
comment 'string':表示注釋信息。

10、刪除存儲過程、函數

drop {procedure | function} [if exists] sp_name

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM