MySQL存儲過程


        一個存儲過程是一個可編程的函數,它在數據庫中創建並保存。它可以有SQL語句和一些特殊的控制結構組成。

        存儲過程通常有以下優點:

       (1)存儲過程在服務器端運行,執行速度快。

       (2)存儲過程執行一次后,其執行規划就駐留在高速緩沖存儲器,在以后的操作中,只需從高速緩沖存儲器中調用已編譯好的二進制代碼執行,提高了系統性能。

       (3)確保數據庫的安全。使用存儲過程可以完成所有數據庫操作,並可通過編程方式控制上述操作對數據庫信息訪問的權限。

        (4) 存儲過程能過減少網絡流量。針對同一個數據庫對象的操作(如查詢、修改),如果這一操作所涉及的Transaction-SQL語句被組織程存儲過程,那么當在客戶計算機上調用該存儲過程時,網絡中傳送的只是該調用語句,從而大大增加了網絡流量並降低了網絡負載。

 

1.格式

        在MySQL 5.1中創建存儲過程,必須具有CREATE routine權限。

        CREATE PROCEDURE的語法格式:

        CREATE PROCEDURE sp_name ([proc_parameter[,...]])

           [characteristic ...] routine_body

        其中,proc_parameter的參數如下:

                 [ IN | OUT | INOUT ] param_name type

        characteristic特征如下:

          language SQL

         | [NOT] DETERMINISTIC

         | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }

         | SQL SECURITY { DEFINER | INVOKER }

         | COMMENT 'string'

 

        MySQL存儲過程創建的簡要描述:

CREATE PROCEDURE存儲過程名 (參數列表)
   BEGIN
         SQL語句代碼塊
END

        1.由括號包圍的參數列必須總是存在。如果沒有參數,也該使用一個空參數列()。每個參數默認都是一個IN參數。要指定為其它參數,可在參數名之前使用關鍵詞 OUT或INOUT

        2.過程體的開始與結束使用BEGIN與END進行標識, 里面包含了在過程調用的時候必須執行的語句。當存儲過程體中只有一個SQL語句時可以省略BEGIN-END標志。

        3.在mysql客戶端定義存儲過程的時候使用delimiter命令來把語句定界符從;變為//。

        當使用delimiter命令時,應該避免使用反斜杠(‘"’)字符,因為那是MySQL的轉義字符。

例:

mysql> delimiter //

mysql> create procedure proc1(out s int)

    -> begin

    -> select count(*) into s from tb;

    -> end

    -> //

Query OK, 0 rows affected (0.70 sec)

        MySQL存儲過程的參數用在存儲過程的定義,共有三種參數類型:IN、OUT、INOUT。

IN 輸入參數:表示該參數的值必須在調用存儲過程時指定,在存儲過程中修改該參數的值不能被返回,為默認值

OUT 輸出參數:該值可在存儲過程內部被改變,並可返回

INOUT 輸入輸出參數:調用時指定,並且可被改變和返回

        小結:如果僅僅想把數據傳給 MySQL 存儲過程,那就使用“in” 類型參數;如果僅僅從 MySQL 存儲過程返回值,那就使用“out” 類型參數;如果需要把數據傳給 MySQL 存儲過程,還要經過一些計算后再傳回給我們,此時,要使用“inout” 類型參數。

 

characteristic:存儲過程的某些特征設定,下面一一介紹:

        language sql:表明編寫這個存儲過程的語言為SQL語言,目前來講,MySQL存儲過程還不能用外部編程語言來編寫,也就是說,這個選項可以不指定。將來將會對其擴展,最有可能第一個被支持的語言是PHP。

        deterministic:設置為DETERMINISTIC表示存儲過程對同樣的輸入參數產生相同的結果,設置為NOT DETERMINISTIC則表示會產生不確定的結果。默認為NOTDETERMINISTIC。

        contains SQL:表示存儲過程不包含讀或寫數據的語句。NO SQL表示存儲過程不包含SQL語句。reads SQL DATA表示存儲過程包含讀數據的語句,但不包含寫數據的語句。modifies SQL DATA表示存儲過程包含寫數據的語句。如果這些特征沒有明確給定,默認的是CONTAINS SQL。

        SQL SECURITY:SQL SECURITY特征可以用來指定存儲過程使用創建該存儲過程的用戶(DEFINER)的許可來執行,還是使用調用者(INVOKER)的許可來執行。默認值是DEFINER。

        COMMENT 'string':對存儲過程的描述,string為描述內容。這個信息可以用SHOWCREATE PROCEDURE語句來顯示。

 

1) IN參數例子

mysql> delimiter //

mysql> create procedure proc_in_param(in pin int)

    -> begin

    -> select pin;

    -> set pin = 2;

    -> select pin;

    -> end

    -> //

Query OK, 0 rows affected (0.61 sec)

 

mysql> delimiter ;


執行結果:

mysql> set @pin=1;

Query OK, 0 rows affected (0.00 sec)

 

mysql> call proc_in_param(@pin);

+------+

| pin  |

+------+

|    1 |

+------+

1 row in set (0.61 sec)

 

+------+

| pin  |

+------+

|    2 |

+------+

1 row in set (0.63 sec)

 

Query OK, 0 rows affected (0.63 sec)
以上可以看出,pin雖然在存儲過程中被修改,但並不影響@pin的值

 

2)OUT參數例子

mysql> delimiter //

mysql> create procedure proc_out_param(out pout int)

    -> begin

    -> select pout;

    -> set pout = 2;

    -> select pout;

    -> end

    -> //

Query OK, 0 rows affected (0.00 sec)

 

mysql> delimiter ;


執行結果:

mysql> set @pout=1;

Query OK, 0 rows affected (0.00 sec)

 

mysql> call proc_out_param(@pout);

+------+

| pout |

+------+

| NULL |

+------+

1 row in set (0.61 sec)

 

+------+

| pout |

+------+

|    2 |

+------+

1 row in set (0.61 sec)

 

Query OK, 0 rows affected (0.61 sec)


3)INOUT參數例子

mysql> delimiter //

mysql> create procedure proc_inout_param(inout pinout int)

    -> begin

    -> select pinout;

    -> set pinout = 2;

    -> select pinout;

    -> end

    -> //

Query OK, 0 rows affected (0.00 sec)

 

mysql> delimiter ; 

執行結果:

mysql> set @pinout = 1;

Query OK, 0 rows affected (0.00 sec)

 

mysql> call proc_inout_param(@pinout);

+--------+

| pinout |

+--------+

|      1 |

+--------+

1 row in set (0.00 sec)

 

+--------+

| pinout |

+--------+

|      2 |

+--------+

1 row in set (0.02 sec)

 

Query OK, 0 rows affected (0.02 sec)

 

2.變量

Ⅰ. 變量定義

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

其中,type為MySQL的數據類型,如:int、float、date、varchar(length)

        DECLARE用來聲明局部變量,且DECLARE僅被用在BEGIN ... END復合語句里,並且必須在復合語句的開頭,在任何其它語句之前。它可以被用在嵌套的塊中,除了那些用相同名字聲明變量的塊。

        要給變量提供一個默認值,請包含一個DEFAULT子句。值可以被指定為一個表達式,不需要為一個常數。如果沒有DEFAULT子句,初始值為NULL

        在存儲過程中聲明局部變量,它們可以用來存儲臨時結果。局部變量和用戶變量的區別在於:局部變量前面沒有使用@符號,局部變量在其所在的BEGIN…END語句塊處理完后就消失了,而用戶變量存在於整個會話當中。

 

例如:

        DECLARE counter INT DEFAULT 0;

        DECLARE v_int int unsigned default 4000000;  

        DECLARE v_numeric number(8,2) DEFAULT 9.95;  

        DECLARE v_date date DEFAULT '2012-12-31';  

        DECLARE v_datetime datetime DEFAULT '2012-01-01 00:00:00';  

        DECLARE v_varchar varchar(255) DEFAULT 'This will not be padded';   

 

 

Ⅱ. 變量賦值

            SET 變量名 = 表達式值 [,variable_name = expression ...]

參考變量可能是子程序內聲明的變量,或者是全局服務器變量。

在存儲程序中的SET語句作為預先存在的SET語法的一部分來實現。這允許SET a=x, b=y, ...這樣的擴展語法。

其中不同的變量類型(局域聲明變量及全局和集體變量)可以被混合起來。

這也允許把局部變量和一些只對系統變量有意義的選項合並起來。

 

Ⅲ. 用戶變量

用戶變量與數據庫連接有關,在這個連接中聲明的變量,在連接斷開的時候,就會消失。

在此連接中聲明的變量無法在另一連接中使用。用戶變量的變量名的形式為@varname的形式。名字必須以@開頭。

變量可以使用使用set語句來賦值,也可以使用可以使用select 語句為變量賦值 。

對於SET,可以使用=或:=作為分配符。分配給每個變量的expr可以為整數、實數、字符串或者NULL值。也可以用語句代替SET來為用戶變量分配一個值。在這種情況下,分配符必須為:=而不能用=,因為在非SET語句中=被視為一個比較 操作符.

mysql里面的變量是不嚴格限制數據類型的,它的數據類型根據你賦給它的值而隨時變化 。

Mysql中的變量分為:用戶變量與系統變量。

系統變量:系統變量又分為全局變量與會話變量。

      全局變量在MYSQL啟動的時候由服務器自動將它們初始化為默認值,這些默認值可以通過更改my.ini這個文件來更改。

       會話變量在每次建立一個新的連接的時候,由MYSQL來初始化。MYSQL會將當前所有全局變量的值復制一份。來做為會話變量。(也就是說,如果在建立會話以后,沒有手動更改過會話變量與全局變量的值,那所有這些變量的值都是一樣的。)

       全局變量與會話變量的區別就在於,對全局變量的修改會影響到整個服務器,但是對會話變量的修改,只會影響到當前的會話。

 

ⅰ. 在MySQL客戶端使用用戶變量

mysql > SELECT 'Hello World' into @x;  

mysql > SELECT @x;  

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

|   @x        |  

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

| Hello World |  

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

 

說明:SELECT col_name[,...] INTO var_name[,...] table_expr

這個SELECT語法把選定的列直接存儲到變量。因此,只有單一的行可以被取回。

例:SELECT id,data INTO x,y FROM tb.t1 LIMIT 1;

SQL變量名不能和列名一樣。如果SELECT ... INTO這樣的SQL語句包含一個對列的參考,並包含一個與列相同名字的局部變量,MySQL當前把參考解釋為一個變量的名字。

 

mysql > SET @y='Goodbye’;  

mysql > SELECT @y;  

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

|     @y              |  

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

| Goodbye |  

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

 

mysql > SET @z=1+2+3;  

mysql > SELECT @z;  

+------+  

| @z   |  

+------+  

|  6   |  

+------+  

 

mysql>set@name = '';

mysql>select @name:=password from user limit 0,1;

 

 

ⅱ. 在存儲過程中使用用戶變量

mysql > CREATE PROCEDURE GreetWorld( ) SELECT CONCAT(@greeting,' World');  

mysql > SET @greeting='Hello';  

mysql > CALL GreetWorld( );  

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

| CONCAT(@greeting,' World') |  

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

|  Hello World               |  

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

 

ⅲ. 在存儲過程間傳遞全局范圍的用戶變量

mysql> CREATE PROCEDURE p1()   SET @last_procedure='p1';  

mysql> CREATE PROCEDURE p2() SELECT CONCAT('Last procedure was ',@last_procedure);  

mysql> CALL p1( );  

mysql> CALL p2( );  

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

| CONCAT('Last procedure was ',@last_proc  |  

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

| Last procedure was p1                         |  

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

  

注意:

①用戶變量名一般以@開頭

②濫用用戶變量會導致程序難以理解及管理

關於變量說明:http://dev.mysql.com/doc/refman/5.1/zh/language-structure.html

 

 

(5). 注釋

 MySQL存儲過程可使用兩種風格的注釋

        雙模杠:--       該風格一般用於單行注釋

        c風格: 一般用於多行注釋

例如:

 mysql > DELIMITER //  

mysql > CREATE PROCEDURE proc1 --name存儲過程名  

-> (IN parameter1 INTEGER)   

-> BEGIN   

-> DECLARE variable1 CHAR(10);   

-> IF parameter1 = 17 THEN   

-> SET variable1 = 'birds';   

-> ELSE 

-> SET variable1 = 'beasts';   

-> END IF;   

-> INSERT INTO table1 VALUES (variable1);  

-> END   

-> //  

mysql > DELIMITER ;  

 

4.      MySQL存儲過程的調用

        用call和你過程名以及一個括號,括號里面根據需要,加入參數,參數包括輸入參數、輸出參數、輸入輸出參數。具體的調用方法可以參看上面的例子。

        語法格式:CALL sp_name([parameter[,...]])

        說明:sp_name為存儲過程的名稱,如果要調用某個特定數據庫的存儲過程,則需要在前面加上該數據庫的名稱。parameter為調用該存儲過程使用的參數,這條語句中的參數個數必須總是等於存儲過程的參數個數。

 

Java代碼調用存儲過程:

java.sql.CallableStatement接口用於調用存儲過程,通過Connection實例的prepareCall()方法返回CallableStatement對象,在prepareCall()內部為一固定寫法{call 存儲過程名(參數列表1,參數列表2)},其中,參數可用?占位,如:

              connection.prepareCall("{call proc(?)}");

存儲過程的輸入參數:通過java.sql.CallableStatement實例的setXXX()方法賦值,用法類似java.sql.PreparedStatement。

存儲過程的輸出參數:通過java.sql.CallableStatement實例的registerOutParameter(參數位置, 參數類型)方法賦值,參數類型使用java.sql.Types中定義的類型。

示例如下:

調用帶輸入參數的存儲過程:

public void exeProc () {

    try {

            callableStatement=connection.prepareCall("{call proc_in_param(?)}");

            callableStatement.setInt(1, 1); //設置輸入參數

            resultSet=callableStatement.executeQuery();//執行存儲過程

            if(resultSet.next()) {

                System.out.println(resultSet.getInt(1)+""t"+resultSet.getString(2));

            }

        } catch (SQLException e) {

            e.printStackTrace();

        }

    }  

 

代碼調用帶輸出參數的存儲過程 (返回數據庫中的記錄數)

public void exeProc2(){

        try {

            callableStatement=connection.prepareCall("{call proc_in_param(?)}");

            //設置輸出參數

            callableStatement.registerOutParameter(1, Types.INTEGER);

            //執行存儲過程

            resultSet=callableStatement.executeQuery();

            if(resultSet.next()) {

                System.out.println(resultSet.getInt(1));

            }

        } catch (SQLException e) {

            e.printStackTrace();

        }

    }

 

 

5.      MySQL存儲過程的查詢

       查看某個數據庫下面的存儲過程:

               select name from mysql.proc where db=’數據庫名’;

       或者

                 select routine_name from information_schema.routines where routine_schema='數據庫名';

       或者

                  show procedure status where db='數據庫名';

        進行查詢。

        查看存儲過程的詳細信息:

                   SHOW CREATE PROCEDURE 數據庫.存儲過程名;

 

6.      MySQL存儲過程的修改

              ALTER PROCEDURE sp_name [characteristic ...]

        其中,characteristic為:

                { CONTAINS SQL | NO SQL | READS SQLDATA | MODIFIES SQL DATA }

                 | SQL SECURITY { DEFINER | INVOKER }

                 | COMMENT 'string'

        說明:characteristic是存儲過程創建時的特征

 

        注意:該語句只能修改存儲過程的特性,不能修改代碼。

例:使用alter procedure p2() select concat('Last procedure was',@last_procedure); 該語句與創建時語句相似,但執行會拋出錯誤。

        如果要修改存儲過程的內容(即過程體),可以使用先刪除再重新定義存儲過程的方法。

 

7.      MySQL存儲過程的刪除

              DROP PROCEDURE [if exists] sp_name

        在此之前,必須確認該存儲過程沒有任何依賴關系,否則會導致其他與之關聯的存儲過程無法運行。

 

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 ;

 

mysql> call proc3();

+-------+

| x1    |

+-------+

| inner |

+-------+

1 row in set (0.05 sec)

 

+-------+

| x1    |

+-------+

| outer |

+-------+

1 row in set (0.05 sec)

 

 (2). 條件語句

Ⅰ. if-then -else語句

        可根據不同的條件執行不同的操作。

        語法格式為:

                IF 判斷的條件THEN 一個或多個SQL語句

                 [ELSEIF判斷的條件THEN一個或多個SQL語句] ...

                 [ELSE一個或多個SQL語句]

                END IF

        說明:當判斷條件為真時,就執行相應的SQL語句。

IF語句不同於系統的內置函數IF()函數,IF()函數只能判斷兩種情況。

 

mysql > DELIMITER //  

mysql > CREATE PROCEDURE proc2(IN parameter int)  

     -> begin 

     -> declare var int;

     -> declare t int;  

     -> set var=parameter+1;  

     -> if var=0 then 

     -> insert into var 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 ;  

 

例:

mysql> delimiter //

mysql> CREATE PROCEDURE testProc(IN K1 int, IN K2 int, OUT K3 char(6))

      -> BEGIN

      -> IF K1>K2 THEN

      ->     SET K3= '大於';

      -> elseif k1=k2 then

       ->     set k3='等於';

      -> ELSE

      ->     SET K3= '小於';

      -> END IF;

      -> END;

      -> //

mysql> delimiter ;


Ⅱ. case語句:

        語法格式為:

        CASE case_value

           WHEN when_value THEN statement_list

           [WHEN when_value THEN statement_list] ...

           [ELSE statement_list]

        END CASE

        或者:

        CASE

           WHEN search_condition THEN statement_list

           [WHEN search_condition THEN statement_list] ...

           [ELSE statement_list]

        END CASE

        說明:一個CASE語句經常可以充當一個IF-THEN-ELSE語句。

        第一種格式中case_value是要被判斷的值或表達式,接下來是一系列的WHEN-THEN塊,每一塊的when_value參數指定要與case_value比較的值,如果為真,就執行statement_list中的SQL語句。如果前面的每一個塊都不匹配就會執行ELSE塊指定的語句。CASE語句最后以END CASE結束。

        第二種格式中CASE關鍵字后面沒有參數,在WHEN-THEN塊中,search_condition指定了一個比較表達式,表達式為真時執行THEN后面的語句。與第一種格式相比,這種格式能夠實現更為復雜的條件判斷,使用起來更方便。

 

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 > CREATE PROCEDURE proc3 (in parameter int)  

     -> begin 

     -> declare var int;  

     -> set var=parameter+1;  

     -> case   

     -> when var=0 then   

     -> insert into t values(17);  

     -> when var=1 then   

     -> insert into t values(18);  

     -> else   

     -> insert into t values(19);  

     -> end case;  

     -> end;  

     -> //  

mysql > DELIMITER ; 

 

(3). 循環語句

        MySQL支持3條用來創建循環的語句:while、repeat和loop語句。在存儲過程中可以定義0個、1個或多個循環語句。

Ⅰ. while ···· end while:

        WHILE語句語法格式為:

                WHILE search_condition  DO

                        statement_list

                END WHILE

        說明:語句首先判斷search_condition是否為真,不為真則執行statement_list中的語句,然后再次進行判斷,為真則繼續循環,不為真則結束循環。

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:

        repeat語句格式如下:

         REPEAT

             statement_list

        UNTIL search_condition

        END REPEAT

        說明:REPEAT語句首先執行statement_list中的語句,然后判斷search_condition是否為真,為真則停止循環,不為真則繼續循環。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 ;  

 

        說明:REPEAT語句和WHILE語句的區別在於:REPEAT語句先執行語句,后進行判斷;而WHILE語句是先判斷,條件為真時才執行語句。

 
Ⅲ. loop ·····end loop:

        loop循環不需要初始條件,這點和while 循環相似,同時和repeat循環一樣不需要結束條件, leave語句的意義是離開循環。

        LOOP語句語法格式如下:

          LOOP

                 statement_list

        END LOOP

        說明:LOOP允許某特定語句或語句群的重復執行,實現一個簡單的循環構造,statement_list是需要重復執行的語句。在循環內的語句一直重復至循環被退出,退出時通常伴隨着一個LEAVE 語句。

        LEAVE語句經常和BEGIN...END或循環一起使用。結構如下:

        LEAVE label ; label是語句中標注的名字,這個名字是自定義的。加上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:

        iterate語句,它只可以出現在LOOP、REPEAT和WHILE語句內,意為“再次循環”,通過引用復合語句的標號,來從新開始復合語句。

        它的格式為:ITERATE label

        說明:該語句格式與LEAVE差不多,區別在於:LEAVE語句是離開一個循環,而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.數據庫交互

(1)  INTO用於存儲單行記錄的查詢結果,語法:

         SELECT col_name[,...] INTO var_name[,...] table_expr

這個SELECT語法把選定的列直接存儲到變量。因此,只有單一的行可以被取回

 

(2) CURSOR(游標)用於處理多行記錄的查詢結果

        在MySQL中,游標一定要在存儲過程或函數中使用,不能單獨在查詢中使用。

        使用一個游標需要用到4條特殊的語句:DECLARE CURSOR(聲明游標)、OPEN CURSOR(打開游標)、FETCH CURSOR(讀取游標)和CLOSE CURSOR(關閉游標)。

        如果使用了DECLARE CURSOR語句聲明了一個游標,這樣就把它連接到了一個由SELECT語句返回的結果集中。使用OPEN CORSOR語句打開這個游標。接着,可以用FETCH CURSOR語句把產生的結果一行一行地讀取到存儲過程或存儲函數中去。游標相當於一個指針,它指向當前的一行數據,使用FETCH CORSOR語句可以把游標移動到下一行。當處理完所有的行時,使用CLOSE CURSOR語句關閉這個游標。

i. 聲明游標

        語法格式:DECLARE cursor_name cursor for select_statement

        說明:cursor_name是游標的名稱,游標名稱使用與表名同樣的規則。select_statement是一個SELECT語句,返回的是一行或多行的數據。這個語句聲明一個游標,也可以在存儲過程中定義多個游標,但是一個塊中的每一個游標必須有唯一的名字。

        注意:這里的SELECT子句不能有INTO子句。

        下面的定義符合一個游標聲明:

        DECLARE XS_CUR1 CURSOR FOR

           SELECT 學號, 姓名, 性別, 出生日期, 總學分

               FROM XS

               WHERE 專業名 = '計算機';

        注意:游標只能在存儲過程或存儲函數中使用,例中語句無法單獨運行。

 

ii. 打開游標

        聲明游標后,要使用游標從中提取數據,就必須先打開游標。在MySQL中,使用OPEN語句打開游標,其格式為:OPEN cursor_name

        在程序中,一個游標可以打開多次,由於其他的用戶或程序本身已經更新了表,所以每次打開結果可能不同。

 

iii. 讀取數據

        游標打開后,就可以使用fetch…into語句從中讀取數據。

        語法格式:FETCH cursor_name INTO var_name [, var_name] ...

        說明:FETCH ...INTO語句與SELECT...INTO語句具有相同的意義,FETCH語句是將游標指向的一行數據賦給一些變量,子句中變量的數目必須等於聲明游標時SELECT子句中列的數目。var_name是存放數據的變量名。

 

iv. 關閉游標

        游標使用完以后,要及時關閉。關閉游標使用CLOSE語句,格式為:

                CLOSE cursor_name語句參數的含義與OPEN語句中相同。

例: 創建一個測試表:create table curtest(name varchar(20));

        增加一些測試數據:

mysql> insert into curtest values('t1');

mysql> insert into curtest values('t2');

mysql> insert into curtest values('t3');

mysql> insert into curtest values('t4');

mysql> insert into curtest values('t5');

         創建存儲過程:

mysql> delimiter //

mysql>  /*建立 存儲過程 create */

mysql>  CREATE PROCEDURE useCursor()

    ->     BEGIN

    ->     /*局部變量的定義 declare*/

    ->          declare tmpName varchar(20) default '' ;

    ->          declare allName varchar(255) default '' ;

    ->          declare cur1 CURSOR FOR SELECT name FROM curtest ;

    ->          declare CONTINUE HANDLER FOR SQLSTATE '02000' SET tmpname = null;

    ->

    ->     /*開游標*/

    ->        OPEN cur1;

    ->

    ->          /*游標向下走一步*/

    ->          FETCH cur1 INTO tmpName;

    ->

    ->          /* 循環體  把游標查詢出的 name 都加起並用 ; 號隔開 */

    ->        WHILE ( tmpname is not null) DO

    ->           set tmpName = CONCAT(tmpName ,";") ;

    ->           set allName = CONCAT(allName ,tmpName) ;

    ->

    ->         /*游標向下走一步*/

    ->         FETCH cur1 INTO tmpName;

    ->       END WHILE;

    ->       CLOSE cur1;

    ->

    ->     select allName ;

    -> END

    -> //

mysql> delimiter ;

執行存儲過程:

mysql> call useCursor();

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

| allName         |

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

| t1;t2;t3;t4;t5; |

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

1 row in set (0.08 sec)

 

例:

CURSOR用於處理多行記錄的查詢結果

DELIMITER //

DROP PROCEDURE IF EXITS cursor_example //

CREATE PROCEDURE cursor_example() 

     READS SQL DATA 

BEGIN 

     DECLARE l_employee_id INT; 

     DECLARE l_salary NUMERIC(8,2); 

     DECLARE l_department_id INT;  

     DECLARE done INT DEFAULT 0; 

     DECLARE cur1 CURSOR FOR SELECT employee_id, salary, department_id FROM employees; 

     DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1; 

 

     OPEN cur1; 

     emp_loop: LOOP 

         FETCH cur1 INTO l_employee_id, l_salary, l_department_id; 

         IF done=1 THEN 

             LEAVE emp_loop; 

         END IF; 

     END LOOP emp_loop; 

     CLOSE cur1; 

END

//

DELIMITER ; 

 

unbounded SELECT語句用於存儲過程返回結果集,例:

 DELIMITER //

DROP PROCEDURE IF EXISTS sp_emps_in_dept //

CREATE PROCEDURE sp_emps_in_dept(in_employee_id INT) 

BEGIN 

     SELECT employee_id, surname, firstname, address1, address2, zipcode, date_of_birth FROM employees WHERE department_id=in_employee_id; 

END

 //

DELIMITER ;  

 

 

UPDATE、INSERT、DELETE、CREATE TABLE等非查詢語句也可以嵌入存儲過程里,例:

 DELIMITER //

DROP PROCEDURE IF EXITS sp_update_salary //

CREATE PROCEDURE sp_update_salary(in_employee_id INT, in_new_salary NUMERIC(8,2))  

BEGIN  

     IF in_new_salary < 5000 OR in_new_salary > 500000 THEN  

         SELECT "Illegal salary: salary must be between $5000 and $500, 000";  

     ELSE  

         UPDATE employees SET salary=in_new_salary WHERE employee_id=in_employee_id;  

     END IF; 

END  

//

DELIMITER ;  

 

使用CALL調用存儲程序,例:

DELIMITER //  

DROP PROCEDURE IF EXISTS call_example //

CREATE PROCEDURE call_example(employee_id INT, employee_type VARCHAR(20))  

     NO SQL  

BEGIN  

     DECLARE l_bonus_amount NUMERIC(8,2);  

  

     IF employee_type='MANAGER' THEN  

         CALL calc_manager_bonus(employee_id, l_bonus_amount);  

     ELSE  

         CALL calc_minion_bonus(employee_id, l_bonus_amount);  

     END IF;  

     CALL grant_bonus(employee_id, l_bonus_amount);  

END

//

DELIMITER ;

 

 

10.      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 

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

 

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為小數位數]

注:返回類型並非均為整數,如:
(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)                                                             //開平方

 

 

SQL語句中的錯誤提示

        在存儲過程中處理SQL語句可能導致一條錯誤消息,並且MySQL立即停止對存儲過程的處理。每一個錯誤消息都有一個唯一代碼和一個SQLSTATE代碼。例如:

                Error 1022, "Can't write;duplicate(重復) key intable"

                Error 1048, "Column cannot benull"

                Error 1052, "Column is ambiguous(歧義)"

                Error 1062, "Duplicate entry forkey"

        MySQL手冊的“錯誤消息和代碼”一章中列出了所有的出錯消息及它們各自的代碼。http://dev.mysql.com/doc/refman/5.1/zh/error-handling.html

        為了防止MySQL在一條錯誤消息產生時就停止處理,需要使用 DECLARE handler語句。該語句語句為錯誤代碼聲明了一個處理程序,它指明:對一條SQL語句的處理如果導致一條錯誤消息,將會發生什么。

        DECLARE HANDLER語法格式為:

                DECLARE handler_type HANDLER FOR condition_value[,...] sp_statement

        其中,handler_type為:

                 Continue

                | EXIT

        | UNDO

        condition_value為:

                 SQLstate [VALUE] sqlstate_value

                | condition_name

                | SQLwarning

                | NOT FOUND

                | SQLexception

                | mysql_error_code

說明:

●   handler_type:處理程序的類型,主要有三種:CONTINUE、EXIT和UNDO。對CONTINUE處理程序,MySQL不中斷存儲過程的處理。對於EXIT處理程序,當前   BEGIN...END復合語句的執行被終止。UNDO處理程序類型語句暫時還不被支持。

●  condition_value:給出SQLSTATE的代碼表示。

   condition_name是處理條件的名稱。

   SQLWARNING是對所有以01開頭的SQLSTATE代碼的速記。

   NOT FOUND是對所有以02開頭的SQLSTATE代碼的速記。

    SQLEXCEPTION是對所有沒有被SQLWARNING或NOT FOUND捕獲的SQLSTATE代碼的速記。當用戶不想為每個可能的出錯消息都定義一個處理程序時可以使用以上三種形式。

    mysql_error_code是具體的SQLSTATE代碼。除了SQLSTATE值,MySQL錯誤代碼也被支持,表示的形式為:ERROR= 'xxxx'。

●   sp_statement:處理程序激活時將要執行的動作。

        例:首先在表中插入一條數據:insert into tb values('1001','dd',20);

        然后,創建一個存儲過程:

mysql> create procedure exam()

    -> begin

    -> insert into tb values('1001','dd',20);

    -> end

    -> //

mysql> call exam();

ERROR 1062 (23000): Duplicate entry '1001' for key 'PRIMARY'

        再創建另一個存儲過程,如果出現錯誤,讓程序繼續進行。

mysql> DELIMITER //

mysql> CREATE PROCEDURE exam_handler()

    -> BEGIN

    ->    DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2=1;

    ->    SET @x=2;

    ->    INSERT INTO tb VALUES('1001', '張三', 20);

    ->    SET@x=3;

    -> END

    -> //

mysql> DELIMITER ;

        再看執行結果:

mysql> call exam_handler();

Query OK, 0 rows affected (0.01 sec)

 

        說明:在調用存儲過程后,未遇到錯誤消息時處理程序未被激活,當執行INSERT語句出現出錯消息時,MySQL檢查是否為這個錯誤代碼定義了處理程序。如果有,則激活該處理程序,本例中,INSERT語句導致的錯誤消息剛好是SQLSTATE代碼中的一條,接下來執行處理程序的附加語句(SET @x2=1)。此后,MySQL檢查處理程序的類型,這里的類型為CONTINUE,因此存儲過程繼續處理,將用戶變量x賦值為3。如果這里的INSERT語句能夠執行,處理程序將不被激活,用戶變量x2將不被賦值。

        注意:不能為同一個出錯消息在同一個BEGIN-END語句塊中定義兩個或更多的處理程序。

        為了提高可讀性,可以使用DECLARE CONDITION語句為一個SQLSTATE或出錯代碼定義一個名字,並且可以在處理程序中使用這個名字。

        DECLARE CONDITION語法格式為:

                DECLARE condition_name CONDITION FOR condition_value

        其中,condition_value:

                 SQLSTATE [VALUE] sqlstate_value

                | mysql_error_code

        說明:condition_name是處理條件的名稱,condition_value為要定義別名的SQLSTATE或出錯代碼。

例: 修改上例中的存儲過程,將SQLSTATE '23000' 定義成NON_UNIQUE,並在處理程序中使用這個名稱。如:

mysql> DELIMITER //

mysql> CREATE PROCEDURE exam_handler()

        -> BEGIN

        -> DECLARE NON_UNIQUE CONTINUE FOR SQLSTATE '23000';

        -> DECLARE HANDLER FOR NON_UNIQUE SET @x2=1;

        -> SET @x=2;

        -> INSERT INTO tb VALUES('1001', '張三', 20);

        -> SET@x=3;

        -> END

        -> //

mysql>DELIMITER ;

 

關於存儲過程的官方文檔:http://dev.mysql.com/doc/refman/5.1/zh/stored-procedures.html


免責聲明!

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



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