一個存儲過程是一個可編程的函數,它在數據庫中建立並保存。它能夠有SQL語句和一些特殊的控制結構組成。html
存儲過程一般有如下優勢:前端
(1)存儲過程在服務器端運行,執行速度快。java
(2)存儲過程執行一次後,其執行規劃就駐留在高速緩衝存儲器,在之後的操做中,只需從高速緩衝存儲器中調用已編譯好的二進制代碼執行,提升了系統性能。mysql
(3)確保數據庫的安全。使用存儲過程能夠完成全部數據庫操做,並可經過編程方式控制上述操做對數據庫信息訪問的權限。sql
(4) 存儲過程能過減小網絡流量。針對同一個數據庫對象的操做(如查詢、修改),若是這一操做所涉及的Transaction-SQL語句被組織程存儲過程,那麼當在客戶計算機上調用該存儲過程時,網絡中傳送的只是該調用語句,從而大大增長了網絡流量並下降了網絡負載。數據庫
1.格式express
在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