在MySQL存儲過程的語句中有三個標准的循環方式:WHILE循環,LOOP循環以及REPEAT循環。還有一種非標准的循環方式:GOTO,不過這種循環方式最好別用,很容易引起程序的混亂,在這里就不錯具體介紹了。
這幾個循環語句的格式如下:
- WHILE……DO……END WHILE
- REPEAT……UNTIL END REPEAT
- LOOP……END LOOP
- GOTO
下面首先使用第一種循環編寫一個例子。
mysql> create procedure pro10() -> begin -> declare i int; -> set i=0; -> while i<5 do -> insert into t1(filed) values(i); -> set i=i+1; -> end while; -> end;//
Query OK, 0 rows affected (0.00 sec)
在這個例子中,INSERT和SET語句在WHILE和END WHILE之間,當變量i大於等於5的時候就退出循環。使用set i=0;語句是為了防止一個常見的錯誤,如果沒有初始化,i默認變量值為NULL,而NULL和任何值操作的結果都是NULL。
執行一下這個存儲過程並產看一下執行結果:
mysql> delete from t1//
Query OK, 0 rows affected (0.00 sec)
mysql> call pro10()//
Query OK, 1 row affected (0.00 sec)
mysql> select * from t1//
+——-+
| filed |
+——-+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
+——-+
5 rows in set (0.00 sec)
以上就是執行結果,有5行數據插入到數據庫中,證明存儲過程編寫正確無誤^_^。
再來看一下第二個循環控制指令 REPEAT……END REPEAT。使用REPEAT循環控制語句編寫下面這個存儲過程:
mysql> create procedure pro11() -> begin -> declare i int default 0; -> repeat -> insert into t1(filed) values(i); -> set i=i+1; -> until i>=5 -> end repeat; -> end;//
Query OK, 0 rows affected (0.00 sec)
這個REPEAT循環的功能和前面WHILE循環一樣,區別在於它的執行后檢查是否滿足循環條件(until i>=5),而WHILE則是執行前檢查(while i<5 do)。
不過要注意until i>=5后面不要加分號,如果加分號,就是提示語法錯誤。
編寫完成后,調用一下這個存儲過程,並查看結果:
mysql> delete from t1//
Query OK, 5 rows affected (0.00 sec)
mysql> call pro11()//
Query OK, 1 row affected (0.00 sec) #雖然在這里顯示只有一行數據受到影響,但是下面選擇數據的話,還是插入了5行數據。
mysql> select * from t1//
+——-+
| filed |
+——-+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
+——-+
5 rows in set (0.00 sec)
一行就是執行結果,實際的作用和使用while編寫的存儲過程一樣,都是插入5行數據。
再來看一下第三個循環控制語句LOOP……END LOOP。編寫一個存儲過程程序如下:
mysql> create procedure pro12() -> begin -> declare i int default 0; -> loop_label: loop -> insert into t1(filed) values(i); -> set i=i+1; -> if i>=5 then -> leave loop_label; -> end if; -> end loop; -> end;//
從上面這個例子可以看出,使用LOOP編寫同樣的循環控制語句要比使用while和repeat編寫的要復雜一些:在循環內部加入了IF……END IF語句,在IF語句中又加入了LEAVE語句,LEAVE語句的意思是離開循環,LEAVE的格式是:LEAVE 循環標號。
編寫完存儲過程程序后,來執行並查看一下運行結果:
mysql> delete from t1//
Query OK, 5 rows affected (0.00 sec)
mysql> call pro12//
Query OK, 1 row affected (0.00 sec) #雖然說只有一行數據受影響,但是實際上是插入了5行數據。
mysql> select * from t1//
+——-+
| filed |
+——-+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
+——-+
5 rows in set (0.00 sec)
執行結果和使用WHILE、LOOP編寫的循環一樣,都是往標中插入5行值。
Labels 標號和 END Labels 結束標號
在使用loop的時候,使用到的labels標號,對於labels可以用到while,loop,rrepeat等循環控制語句中。而且有必要好好認識一下lables!!
mysql> create procedure pro13()
-> label_1:begin
-> label_2:while 0=1 do leave label_2;end while;
-> label_3:repeat leave label_3;until 0=0 end repeat;
-> label_4:loop leave label_4;end loop;
-> end;//
Query OK, 0 rows affected (0.00 sec)
上面這里例子顯示了可以在BEGIN、WHILE、REPEAT或者LOOP語句前使用語句標號,語句標號只能在合法的語句前使用,所以LEAVE label_3意味着離開語句標號名為label_3的語句或符合語句。
其實,也可以使用END labels來表示標號結束符。
mysql> create procedure pro14()
-> label_1:begin
-> label_2:while 0=1 do leave label_2;end while label_2;
-> label_3:repeat leave label_3;until 0=0 end repeat label_3;
-> label_4:loop leave label_4;end loop label_4;
-> end label_1;//
Query OK, 0 rows affected (0.00 sec)
上面就是使用了標號結束符,其實這個結束標號並不是十分有用,而且他必須和開始定義的標號名字一樣,否則就會報錯。如果要養成一個良好的編程習慣方便他人閱讀的話,可以使用這個標號結束符。
ITERATE 迭代
如果是在ITERATE語句,即迭代語句中的話,就必須使用LEAVE語句。ITERATE只能出現在LOOP,REPEAT和WHILE語句中,它的意思是“再次循環”,例如:
mysql> create procedure pro15()
-> begin
-> declare i int default 0;
-> loop_label:loop
-> if i=3 then
-> set i=i+1;
-> iterate loop_label;
-> end if;
-> insert into t1(filed) values(i);
-> set i=i+1;
-> if i>=5 then
-> leave loop_label;
-> end if;
-> end loop;
-> end;//
Query OK, 0 rows affected (0.00 sec)
iterate語句和leave語句一樣,也是在循環內部使用,它有點類似於C/C++語言中的continue。
那么這個存儲程序是怎么運行的的?首先i的值為0,條件判斷語句if i=3 then判斷為假,跳過if語段,向數據庫中插入0,然后i+1,同樣后面的if i>=5 then判斷也為假,也跳過;繼續循環,同樣插入1和2;在i=3的時候條件判斷語句if i=3 then判斷為真,執行i=i+1,i值為4,然后執行迭代iterate loop_label;,即語句執行到iterate loop_label;后直接跳到if i=3 then判斷語句,執行判斷,這個時候由於i=4,if i=3 then判斷為假,跳過IF語段,將4添加到表中,i變為5,條件判斷if i>=5 then判斷為真,執行leave loop_label;跳出loop循環,然后執行end;//,結束整個存儲過程。
綜上所述,數據庫中將插入數值:0,1,2,4。執行存儲過程,並查看結果:|
mysql> delete from t1//
Query OK, 5 rows affected (0.00 sec)
mysql> call pro15//
Query OK, 1 row affected (0.00 sec)
mysql> select * from t1//
+——-+
| filed |
+——-+
| 0 |
| 1 |
| 2 |
| 4 |
+——-+
4 rows in set (0.00 sec)
和我們上面分析的結果一樣,只插入了數值0,1,2,4。
存儲過程如同一門程序設計語言,同樣包含了數據類型、流程控制、輸入和輸出和它自己的函數庫。
--------------------基本語法--------------------
一.創建存儲過程
create procedure sp_name()
begin
.........
end
二.調用存儲過程
1.基本語法:call sp_name()
注意:存儲過程名稱后面必須加括號,哪怕該存儲過程沒有參數傳遞
三.刪除存儲過程
1.基本語法:
drop procedure sp_name//
2.注意事項
(1)不能在一個存儲過程中刪除另一個存儲過程,只能調用另一個存儲過程
四.其他常用命令
1.show procedure status
顯示數據庫中所有存儲的存儲過程基本信息,包括所屬數據庫,存儲過程名稱,創建時間等
2.show create procedure sp_name
顯示某一個MySQL存儲過程的詳細信息
--------------------數據類型及運算符--------------------
一、基本數據類型:
略
二、變量:
自定義變量:DECLARE a INT ; SET a=100; 可用以下語句代替:DECLARE a INT DEFAULT 100;
變量分為用戶變量和系統變量,系統變量又分為會話和全局級變量
用戶變量:用戶變量名一般以@開頭,濫用用戶變量會導致程序難以理解及管理
1、 在mysql客戶端使用用戶變量
mysql> SELECT 'Hello World' into @x;
mysql> SELECT @x;
mysql> SET @y='Goodbye Cruel World';
mysql> select @y;
mysql> SET @z=1+2+3;
mysql> select @z;
2、 在存儲過程中使用用戶變量
mysql> CREATE PROCEDURE GreetWorld( ) SELECT CONCAT(@greeting,' World');
mysql> SET @greeting='Hello';
mysql> CALL GreetWorld( );
3、 在存儲過程間傳遞全局范圍的用戶變量
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( );
三、運算符:
1.算術運算符
+ 加 SET var1=2+2; 4
- 減 SET var2=3-2; 1
* 乘 SET var3=3*2; 6
/ 除 SET var4=10/3; 3.3333
DIV 整除 SET var5=10 DIV 3; 3
% 取模 SET var6=10%3 ; 1
2.比較運算符
> 大於 1>2 False
< 小於 2<1 False
<= 小於等於 2<=2 True
>= 大於等於 3>=2 True
BETWEEN 在兩值之間 5 BETWEEN 1 AND 10 True
NOT BETWEEN 不在兩值之間 5 NOT BETWEEN 1 AND 10 False
IN 在集合中 5 IN (1,2,3,4) False
NOT IN 不在集合中 5 NOT IN (1,2,3,4) True
= 等於 2=3 False
<>, != 不等於 2<>3 False
<=> 嚴格比較兩個NULL值是否相等 NULL<=>NULL True
LIKE 簡單模式匹配 "Guy Harrison" LIKE "Guy%" True
REGEXP 正則式匹配 "Guy Harrison" REGEXP "[Gg]reg" False
IS NULL 為空 0 IS NULL False
IS NOT NULL 不為空 0 IS NOT NULL True
3.邏輯運算符
4.位運算符
| 或
& 與
<< 左移位
>> 右移位
~ 非(單目運算,按位取反)
注釋:
mysql存儲過程可使用兩種風格的注釋
雙橫杠:--
該風格一般用於單行注釋
c風格:/* 注釋內容 */ 一般用於多行注釋
--------------------流程控制--------------------
一、順序結構
二、分支結構
if
case
三、循環結構
for循環
while循環
loop循環
repeat until循環
注:
區塊定義,常用
begin
......
end;
也可以給區塊起別名,如:
lable:begin
...........
end lable;
可以用leave lable;跳出區塊,執行區塊以后的代碼。
可以提前退出存儲過程(函數直接return)。
begin和end如同C語言中的{ 和 }。
--------------------輸入和輸出--------------------
mysql存儲過程的參數用在存儲過程的定義,共有三種參數類型,IN,OUT,INOUT
Create procedure|function([[IN |OUT |INOUT ] 參數名 數據類形...])
IN 輸入參數
表示該參數的值必須在調用存儲過程時指定,在存儲過程中修改該參數的值不能被返回,為默認值
OUT 輸出參數
該值可在存儲過程內部被改變,並可返回
INOUT 輸入輸出參數
調用時指定,並且可被改變和返回
IN參數例子:
CREATE PROCEDURE sp_demo_in_parameter(IN p_in INT)
BEGIN
SELECT p_in; --查詢輸入參數
SET p_in=2; --修改
select p_in;--查看修改后的值
END;
執行結果:
mysql> set @p_in=1
mysql> call sp_demo_in_parameter(@p_in)
略
mysql> select @p_in;
略
以上可以看出,p_in雖然在存儲過程中被修改,但並不影響@p_id的值
OUT參數例子
創建:
mysql> CREATE PROCEDURE sp_demo_out_parameter(OUT p_out INT)
BEGIN
SELECT p_out;/*查看輸出參數*/
SET p_out=2;/*修改參數值*/
SELECT p_out;/*看看有否變化*/
END;
執行結果:
mysql> SET @p_out=1
mysql> CALL sp_demo_out_parameter(@p_out)
略
mysql> SELECT @p_out;
略
INOUT參數例子:
mysql> CREATE PROCEDURE sp_demo_inout_parameter(INOUT p_inout INT)
BEGIN
SELECT p_inout;
SET p_inout=2;
SELECT p_inout;
END;
執行結果:
set @p_inout=1
call sp_demo_inout_parameter(@p_inout) //
略
select @p_inout;
略
附:函數庫
mysql存儲過程基本函數包括:字符串類型,數值類型,日期類型
一、字符串類
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
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)
TRIM([[BOTH|LEADING|TRAILING] [padding] FROM]string2) //去除指定位置的指定字符
UCASE (string2 ) //轉換成大寫
RIGHT(string2,length) //取string2最后length個字符
SPACE(count) //生成count個空格
二、數值類型
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 ) //返回符號,正負或0
SQRT(number2) //開平方
三、日期類型
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) //分
注:可用在INTERVAL中的類型:DAY ,DAY_HOUR ,DAY_MINUTE ,DAY_SECOND ,HOUR ,HOUR_MINUTE ,HOUR_SECOND ,MINUTE ,MINUTE_SECOND,MONTH ,SECOND ,YEAR
DECLARE variable_name [,variable_name...] datatype [DEFAULT value];
其中,datatype為mysql的數據類型,如:INT, FLOAT, DATE, VARCHAR(length)
例:
DECLARE l_int INT unsigned default 4000000;
DECLARE l_numeric NUMERIC(8,2) DEFAULT 9.95;
DECLARE l_date DATE DEFAULT '1999-12-31';
DECLARE l_datetime DATETIME DEFAULT '1999-12-31 23:59:59';
DECLARE l_varchar VARCHAR(255) DEFAULT 'This will not be padded';
ORACLE與MYSQL的存儲過程/函數的使用區別
-
編號 類別 ORACLE MYSQL 注釋 1 創建存儲過程語句不同 create or replace procedure P_ADD_FAC(
id_fac_cd IN ES_FAC_UNIT.FAC_CD%TYPE) isDROP PROCEDURE IF EXISTS `SD_USER_P_ADD_USR`;
create procedure P_ADD_FAC(
id_fac_cd varchar(100))
1.在創建存儲過程時如果存在同名的存儲過程,會刪除老的存儲過程.
oracle使用create or replace.
mysql使用先刪除老的存儲過程,然后再創建新的存儲過程.
2. oracle 存儲過程可以定義在package中,也可以定義在Procedures中. 如果定義在包中,一個包中可以包含多個存儲過程和方法.如果定義在Procedures中,存儲過程中不可以定義多個存儲過程.
Mysql 存儲過程中不可以定義多個存儲過程.
3. oracle中字符串類型可以使用varchar2.
Mysql 需要使用varchar
4. Oracle中參數varchar長度不是必須的,
Mysql中參數varchar長度是必須的, 比如varchar(100)2 創建函數語句不同 CREATE OR REPLACEFUNCTION F_ROLE_FACS_GRP(
ii_role_int_key IN SD_ROLE.ROLE_INT_KEY%TYPE
) RETURN VARCHAR2DROP FUNCTION IF EXISTS `SD_ROLE_F_ROLE_FACS_GRP`;
CREATE FUNCTION `SD_ROLE_F_ROLE_FACS_GRP`(
ii_role_int_key INTEGER(10)
) RETURNS varchar(1000)1.在創建函數時如果存在同名的函數,會刪除老的函數.
oracle使用create or replace.
mysql使用先刪除老的函數,然后再創建新的函數.
2. oracle 函數可以定義在package中,也可以定義在Functions中. 如果定義在包中,一個包中可以包含多個存儲過程和函數.如果定義在Functions中,每個函數只能定義一個函數.
Mysql Functions不可以定義多個函數.
3. oracle返回值用return.
Mysql返回值用returns.3 傳入參數寫法不同 procedure P_ADD_FAC(
id_fac_cd IN ES_FAC_UNIT.FAC_CD%TYPE)create procedure P_ADD_FAC(
(in) id_fac_cd varchar(100))
1. oracle存儲過程參數可以定義為表的字段類型.
Mysql存儲過程不支持這種定義方法.需要定義變量的實際類型和長度.
2. oracle 參數類型in/out/inout寫在參數名后面.
Mysql 參數類型in/out/inout寫在參數名前面.
3. oracle 參數類型in/out/inout 都必須寫.
Mysql 參數類型如果是in,則可以省略. 如果是out或inout則不能省略.
注意: mysql中指定參數為IN, OUT, 或INOUT 只對PROCEDURE是合法的。(FUNCTION參數總是被認為是IN參數) RETURNS字句只能對FUNCTION做指定,對函數而言這是強制的。它用來指定函數的返回類型,而且函數體必須包含一個RETURN value語句。function func_name(
gw_id in(out) varchar2 )create function func_name(
gw_id varchar(100))4 包的聲明方式 create or replace package/package body package name 拆分成多個存儲過程或函數 oracle可以創建包,包中可以包含多個存儲過程和方法.
mysql沒有沒有包這個概念,可以分別創建存儲過程和方法. 每個存儲過程或方法都需要放在一個文件中.
例1: 方法命名
oracle 中SD_FACILITY_PKG.F_SEARCH_FAC
to mysql SD_FACILITY_F_SEARCH_FAC
例2: 過程命名
oracle 中SD_FACILITY_PKG.P_ADD_FAC
to mysql SD_FACILITY_P_ADD_FAC5 存儲過程返回語句不一樣 return; LEAVE proc; (proc 代表最外層的begin end) oracle存儲過程和方法都可以使用return退出當前過程和方法.
Mysql存儲過程中只能使用leave退出當前存儲過程.不可以使用return.
Mysql方法可以使用return退出當前方法.6 存儲過程異常處理不一樣 EXCEPTION
WHEN OTHERS THEN
ROLLBACK ;
ov_rtn_msg := c_sp_name||'('|| li_debug_pos ||'):'||
TO_CHAR(SQLCODE)||': '||SUBSTR(SQLERRM,1,100);DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK ;
set ov_rtn_msg = concat(c_sp_name,'(', li_debug_pos ,'):',
TO_CHAR(SQLCODE),': ',SUBSTR(SQLERRM,1,100));
END;oracle : 內部異常不需要定義,在存儲過程或函數末尾寫上EXCEPTION后,后面的部分即為異常處理的部分. oracle可以定義自定義異常,自定義異常需要使用raise關鍵字拋出異常后,才可以在EXCEPTION中捕獲.
mysql: mysql內部異常也需要先定義,在定義的同時也需要實現異常的功能.
目前mysql不支持自定義異常.7 過程和函數的聲明變量的位置不同 聲明變量在begin…end體之前 聲明變量在begin...end體內,begin之后其他任何內容之前 8 NO_DATA_FOUND異常處理 EXCEPTION
WHEN NO_DATA_FOUND THEN
oi_rtn_cd := 1;
ov_rtn_msg := SD_COMMON.P_GET_MSG('DP-CBM-01100a-016',
li_sub_rtn_cd,
lv_sub_rtn_msg
);使用FOUND_ROWS()代替NO_DATA_FOUND. 詳見注釋. oracle中:
NO_DATA_FOUND是游標的一個屬性.
當select沒有查到數據就會出現 no data found 的異常,程序不會向下執行.
Mysql:
沒有NO_DATA_FOUND這個屬性.但可是使用FOUND_ROWS()方法得到select語句查詢出來的數據.如果FOUND_ROWS()得到的值為0,就進入異常處理邏輯.9 在存儲過程中調用存儲過程方式的不同 Procedure_Name(參數); Call Procedure_Name(參數); MYSQL存儲過程調用存儲過程,需要使用Call pro_name(參數).
Oracle調用存儲過程直接寫存儲過程名就可以了.10 拋異常的方式不同 RAISE Exception_Name; 見備注 詳見<<2009002-OTMPPS-Difficult Questions-0001.doc>>中2.5 Mysql異常處理部分 -
異常處理
- MySQL的GET DIAGNOSTICS語句
這是一個把我困擾已久的問題,今天偶然間解決了。
以前用Oracle時經常會用到的三個東西:sql%rowcount、SQLCODE、SQLERRM
sql%rowcount用於記錄最近一條DML語句修改的記錄條數,就如你在sqlplus下執行delete from之后提示已刪除xx行一樣。
SQLCODE和SQLERRM是Oracle的異常處理函數,常被用於得到完整錯誤提示信息,方便錯誤時處理。
那么問題來了,MySQL有沒有相似的功能呢?以前查了很久也沒有找到好的解決辦法,然而在5.6.4以后,MySQL提供了GET DIAGNOSTICS語法,那么我的問題也隨之迎刃而解。
簡單講GET DIAGNOSTICS能提供以下兩種信息:
語句信息,例如錯誤信息號或者語句影響的行數。
錯誤信息,例如錯誤號和錯誤消息。
如果一條語句產生了三種錯誤,診斷區域包含的語句和錯誤信息類似這樣:
Statement information: row count ... other statement information items ... Condition area list: Condition area 1: error code for condition 1 error message for condition 1 ... other condition information items ... Condition area 2: error code for condition 2: error message for condition 2 ... other condition information items ... Condition area 3: error code for condition 3 error message for condition 3 ... other condition information items ...
使用GET DIAGNOSTICS需要注意的是,它或者包含語句信息,或者包含錯誤信息,但一個GET DIAGNOSTICS不會同時包含語句信息和錯誤信息,所以需要用兩個GET DIAGNOSTICS來獲得語句信息和錯誤信息。
獲得語句信息:
GET DIAGNOSTICS @p1 = NUMBER, @p2 = ROW_COUNT;
獲得錯誤信息:
GET DIAGNOSTICS CONDITION 1 @p3 = RETURNED_SQLSTATE, @p4 = MESSAGE_TEXT;
語句信息條目名稱有:
NUMBER
| ROW_COUNT
錯誤信息條目名稱有:
CLASS_ORIGIN
| SUBCLASS_ORIGIN
| RETURNED_SQLSTATE
| MESSAGE_TEXT
| MYSQL_ERRNO
| CONSTRAINT_CATALOG
| CONSTRAINT_SCHEMA
| CONSTRAINT_NAME
| CATALOG_NAME
| SCHEMA_NAME
| TABLE_NAME
| COLUMN_NAME
| CURSOR_NAME
為了確保獲得正確的主錯誤信息,必須使用類似如下的語句:
GET DIAGNOSTICS @cno = NUMBER;
GET DIAGNOSTICS CONDITION @cno @errno = MYSQL_ERRNO;
最后來看一個使用GET DIAGNOSTICS完整例子(摘自官方文檔) -
BEGIN -- Declare variables to hold diagnostics area information DECLARE code CHAR(5) DEFAULT '00000'; DECLARE msg TEXT; DECLARE rows INT; DECLARE result TEXT; -- Declare exception handler for failed insert DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN GET DIAGNOSTICS CONDITION 1 code = RETURNED_SQLSTATE, msg = MESSAGE_TEXT; END; -- Perform the insert INSERT INTO t1 (int_col) VALUES(value); -- Check whether the insert was successful IF code = '00000' THEN GET DIAGNOSTICS rows = ROW_COUNT; SET result = CONCAT('insert succeeded, row count = ',rows); ELSE SET result = CONCAT('insert failed, error = ',code,', message = ',msg); END IF; -- Say what happened SELECT result; END
結果:insert failed, error = 42S02, message = Table 'ud_omcs.t1' doesn't exist