存儲過程 ,遊標


1 存儲過程簡介

    存儲過程是一段代碼,由存儲在一個數據庫的目錄中、聲明式的和過程式的sql語句組成,可以從一個程序、觸發器或者另一個存儲過程調用它從而激活它。存儲過程(Stored Procedure)是一組爲了完成特定功能的SQL語句集,經編譯後存儲在數據庫中,用戶通過指定存儲過程的名字並給定參數(如果該存儲過程帶有參數)來調用執行它。 
    一個存儲過程是一個可編程的函數,它在數據庫中創建並保存。它可以有SQL語句和一些特殊的控制結構組成。當希望在不同的應用程序或平臺上執行相同的函數,或者封裝特定功能時,存儲過程是非常有用的。數據庫中的存儲過程可以看做是對編程中面向對象方法的模擬。它允許控制數據的訪問方式。
2 存儲過程優缺點
    2.1 優點
        2.1.1  執行速度更快。有兩個原因:首先,在存儲過程創建的時候,數據庫已經對其進行了一次解析和優化,它只在創造時進行編譯,以後每次執行存儲過程都不需再重新編譯,而一般 SQL 語句每執行一次就編譯一次,所以使用存儲過程可提高數據庫執行速度。其次,存儲過程一旦執行,在內存中就會保留一份這個存儲過程,這樣下次再執行同樣的存儲過程時,可以從內存中直接調用。
        2.1.2 減少網絡通信量。調用一個SQL語句不多的存儲過程與直接調用SQL語句的網絡通信量可能不會有很大的差別,但如果存儲過程包含較多SQL語句,那麼其性能絕對比一條一條的調用SQL語句要高得多。
        2.1.3 安全性高。可設定只有某此用戶才具有對指定存儲過程的使用權。
        2.1.4 可維護性高。更新存儲過程通常比更改、測試以及重新部署程序集需要較少的時間和精力。
        2.1.5 不依賴某種宿主語言。如果用多種語言開發,某些通用代碼不用重複。
        2.1.6 布式工作。應用程序和數據庫的編碼工作可以分別獨立進行,而不會相互壓制。
        2.1.7 存儲過程可以重複使用,可減少數據庫開發人員的工作量。
    2.2 缺點
        2.2.1 SQL本身是一種結構化查詢語言,加上了一些控制(賦值、循環和異常處理等),本質上還是過程化的,面對複雜的業務邏輯,過程化的處理會很吃力。這一點算致命傷。
        2.2.2 不便於調試。基本上沒有較好的調試器,很多時候是用print來調試,但用這種方法調試長達數百行的存儲過程簡直是噩夢。
        2.2.3 無法適應數據庫的切割(水平或垂直切割)。數據庫切割之後,存儲過程並不清楚數據存儲在哪個數據庫中。
        2.2.4 精通SQL的新手越來越少。

3 創建存儲過程

    3.1 創建語法如下

CREATE PROCEDURE存儲過程名 (參數列表)
BEGIN
    SQL語句代碼塊
END
    3.2 例如:計算兩個數之和

delimiter //
create procedure procedureAdd (a int, IN b int)
begin
    declare c int;
    if a is null then
        set a = 0;
    end if;
    if b is null then
        set b = 0;
    end if;
    set c = a + b;
    select c as sum;
end;
//
delimiter ;
    3.3 注意:
    由括號包圍的參數列必須總是存在。如果沒有參數,也該使用一個空參數列()。每個參數默認都是一個IN參數,要指定爲其它參數,可在參數名之前使用關鍵詞 OUT或INOUT
    在mysql客戶端定義存儲過程的時候使用delimiter命令來把語句定界符從;變爲//,創建完之後在將分隔符更改爲;號。

4 查看存儲過程

    查看數據庫中所有存儲的存儲過程基本信息,包括所屬數據庫,存儲過程名稱,創建時間等。

show procedure status \G

    或者

select name from mysql.proc where db = 'your_db_name' and 'type' = 'PROCEDURE';

    查看某個已存在的存儲過程的詳細信息(其中procedureAdd爲以創建的存儲過程名)

show create procedure procedureAdd \G

5 調用存儲過程

    5.1 基本語法

call 存儲過程名(參數列表)

    5.2 例如

call procedureAdd(10, 30);

    或者

set @a = 10;
set @b = 30;
call procedureAdd(@a, @b);
    注意:在使用SET定義變量時應遵循SET的語法規則 SET @變量名=初始值;

6 修改存儲過程

    6.1 基本語法

ALTER PROCEDURE 存儲過程名 [characteristic ...]
    注意:characteristic是存儲過程創建時的特徵,在CREATE PROCEDURE語句中已經介紹過。只要設定了其中的值,存儲過程的特徵就隨之變化。
    如果要修改存儲過程的內容,可以使用先刪除再重新定義存儲過程的方法。存儲過程某些的特徵如下
    characteristic:
    COMMENT 'string'
    | LANGUAGE SQL
    | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }

    | SQL SECURITY { DEFINER | INVOKER }

    6.2 例如:修改特徵

    查看默認特徵值

mysql> SELECT SPECIFIC_NAME,SQL_DATA_ACCESS, ROUTINE_COMMENT FROM information_schema.Routines WHERE ROUTINE_NAME='procedureAdd';
+---------------+-----------------+-----------------+
| SPECIFIC_NAME | SQL_DATA_ACCESS | ROUTINE_COMMENT |
+---------------+-----------------+-----------------+
| procedureAdd  | CONTAINS SQL    |                 |
+---------------+-----------------+-----------------+
    將讀寫權限改爲READS SQL DATA,並加上註釋信息'This is a test!',代碼執行如下:

mysql> ALTER  PROCEDURE  procedureAdd READS SQL DATA COMMENT 'This is a test!';
    查看修改後的特徵值

mysql> SELECT SPECIFIC_NAME,SQL_DATA_ACCESS, ROUTINE_COMMENT FROM information_schema.Routines WHERE ROUTINE_NAME='procedureAdd';
+---------------+-----------------+-----------------+
| SPECIFIC_NAME | SQL_DATA_ACCESS | ROUTINE_COMMENT |
+---------------+-----------------+-----------------+
| procedureAdd  | READS SQL DATA  | This is a test! |
+---------------+-----------------+-----------------+
    說明:從查詢的結果可以看出,訪問數據的權限(SQL_DATA_ACCESS)已經變成READS SQL DATA,函數註釋(ROUTINE_COMMENT)已經變成了"This is a test!"。

7 刪除存儲過程

    7.1 基本語法

DROP PROCEDURE  IF  EXISTS 存儲過程名
    7.2 例如

DROP PROCEDURE  IF  EXISTS procedureAdd;

    注意:不能在一個存儲過程中刪除另一個存儲過程,只能調用另一個存儲過程。

*

8 存儲過程局部變量

    8.1 介紹

    在一個存儲過程內部,可以聲明局部變量。他們可以用來存儲中間臨時結果。如果我們在一個存儲過程中需要一個局部變量,必須使用DECLARE VARIABLE語句引入它。通過聲明,就確定了變量的數據類型,並且也可以指定初始值。(如果使用了DECLARE VARIABLE語句,他們必須作爲BEGIN-END語句塊的第一條語句包含其中)

    8.2 例如:

delimiter //
create procedure test(out num1 integer) 
begin 
    declare num2 integer default 100; 
    set num1 = num2; 
end
//
delimiter ;
    調用存儲過程

call test(@num);
select @num;
9 存儲過程和用戶變量

    9.1 介紹

    用戶變量總有一個全局特性,即便它在一個存儲過程內部創建,在存儲過程結束後他們依然保留。在存儲過程之外創建的用戶變量,仍然可以在存儲過程中保留他們自己的值。

    9.2 例如

delimiter //
create procedure user_variable() 
begin 
    set @varTest = 1; 
end
//
delimiter ;
    調用存儲過程後查看varTest值爲1

call user_variable();
select @varTest;
    說明:set語句是sql本身的一部分,它可以講一個值賦給用戶變量和局部變量,也可使用任何隨機表達式。

10 存儲過程與遊標

    10.1 介紹

    常規的select語句可能返回多行,使用遊標(cursor)可以處理這一點,把數據一行一行的取入到存儲過程中。使用遊標需要用到四個特殊語句:declare sursor,open sursor,fetch cursor,和close cursor。

    如果使用declare cursor語句聲明一個遊標,我們就把它連接到了一個表表達式。接下來就可以使用fetch cursor語句來把產生的結果一行一行的獲取到存儲過程中。在某個時刻,結果中只有一行可見,也就是當前行。它就好像是指向結果中一行的一個箭頭,這也是遊標這個名字的來歷。使用fetch cursor這條語句,我們可以把遊標移動到下一行,當處理完所有的行,可以使用close cursor語句來刪除結果。

    10.2 遊標作用及屬性

    作用:
        就是用於對查詢數據庫所返回的記錄進行遍歷,以便進行相應的操作;
    屬性:
        遊標是隻讀的,也就是不能更新它;
        遊標是不能滾動的,也就是只能在一個方向上進行遍歷,不能在記錄之間隨意進退,不能跳過某些記錄;  
        避免在已經打開遊標的表上更新數據。

    10.3 如何使用遊標

    聲明遊標

DECLARE cursor_name CURSOR FOR SELECT語句;  

    打開遊標

OPEN cursor_name; 

    移動遊標

FETCH cursor_name INTO variable list;  

    關閉遊標

CLOSE cursor_name;  

    10.4 遊標實例

    創建測試表及數據

CREATE TABLE test.users (    
ID bigint(20) unsigned NOT NULL AUTO_INCREMENT,    
user_name varchar(60) NOT NULL DEFAULT '',    
user_pass varchar(64) NOT NULL DEFAULT '',    
PRIMARY KEY (ID)   
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

insert into test.users values(1,'name1', 'pass1');
insert into test.users values(2,'name2', 'pass2');
insert into test.users values(3,'name3', 'pass3');
insert into test.users values(4,'name4', 'pass4');
insert into test.users values(5,'name5', 'pass5');

    創建遊標存儲過程

delimiter |
create procedure test_cursor (in param int(10),out result varchar(90))
begin
    declare name varchar(20);
    declare pass varchar(20);
    declare done int;
    declare cur_test CURSOR for select user_name,user_pass from test.users;
    declare continue handler FOR SQLSTATE '02000' SET done = 1;
    if param then
        select concat_ws(',',user_name,user_pass) into result from test.users where id=param;
    else
        open cur_test;
        repeat
            fetch cur_test into name, pass;
            select concat_ws(',',result,name,pass) into result;
            until done end repeat;
        close cur_test;
    end if;
end;|
delimiter ;

    各行命令詳解

    1行,告訴MySQL解釋器,輸入結束命令改爲|,默認爲;(命令本身與存儲過程無關)
    2行,創建一個存儲過程,注意:如果我把out result varchar(90)改成out result varchar,返回的結果中只有一個字符。
    3行,開始
    4行,定義一個變量name
    5行,定義變量pass
    6行,定義一個結束標識
    7行,定義一個光標,指向select user_name,user_pass from test.users;語句
    8行,如果sqlstate等於02000時,把done設置成1,也就是找不到數據時
    9,11,18行,if判斷
    10行,根據參數,把數據取出來,放到result中,concat_ws函數表示concat with separator,即有分隔符的字符串連接,如連接後以逗號分隔
    12行,打開光標
    13,16行,repeat循環,根php的do while原理一樣
    14行,從光標中取出數據。
    15行,將數據合併起來
    17行,關閉光標
    18,19行,標籤閉合。
    20行,

    結果反饋

mysql> call test_cursor(3,@test);                                               
Query OK, 1 row affected (0.00 sec)
mysql> select @test;
+-------------+
| @test       |
+-------------+
| name3,pass3 |
+-------------+
1 row in set (0.00 sec)
mysql> call test_cursor('',@test);
Query OK, 1 row affected, 2 warnings (0.00 sec)
mysql> select @test;
+-------------------------------------------------------------------------+
| @test                                                                   |
+-------------------------------------------------------------------------+
| name1,pass1,name2,pass2,name3,pass3,name4,pass4,name5,pass5,name5,pass5 |
+-------------------------------------------------------------------------+
1 row in set (0.00 sec)



 轉載請註明出處:http://blog.csdn.net/jesseyoung/article/details/34420839


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章