SQL存儲過程(Stored Procedure)

什麼是存儲過程?

  • 存儲過程(procedure)類似於C語言中的函數
  • 用來執行管理任務或應用複雜的業務規則
  • 存儲過程可以帶參數,也可以返回結果
  • 存儲過程可以包含數據操縱語句、變量、邏輯 控制語句等 

存儲過程(Stored Procedure)是一組爲了完成特定功能的SQL語句集,經編譯後存儲在數據庫中,用戶通過指定存儲過程的名字並給定參數(如果該存儲過程帶有參數)來調用執行它。外部程序可以直接調用數據庫裏面定義好的存儲過程,另外數據庫內部的觸發器(trigger)、或者其他存儲過程也可以調用它。

存儲過程的優點

(1)執行速度快:存儲過程創建是就已經通過語法檢查和性能優化,在執行時無需每次編譯。存儲在數據庫服務器,性能高。

(2)允許模塊化設計:只需創建存儲過程一次並將其存儲在數據庫中,以後即可在程序中調用該過程任意次。存儲過程可由在數據庫編程方面有專長的人員創建,並可獨立於程序源代碼而單獨修改 。

(3)提高系統安全性:可將存儲過程作爲用戶存取數據的管道。可以限制用戶對數據表的存取權限,建立特定的存儲過程供用戶使用,完成對數據的訪問。

    存儲過程的定義文本可以被加密,使用戶不能查看其內容。

(4)減少網絡流量:一個需要數百行Transact-SQL代碼的操作由一條執行過程代碼的單獨語句就可實現,而不需要在網絡中發送數百行代碼。

存儲過程的缺點

1、存儲過程會使得數據庫佔用的系統資源加大(cpu、memory),數據庫畢竟主要用來做數據存取的,並不進行復雜的業務邏輯操作。
2、因爲存儲過程依舊是sql,所以沒辦法像編程語言那樣寫出複雜業務邏輯對應的存儲過程。
3、存儲過程不容易進行調試。
4、存儲過程書寫及維護難度都比較大。

 存儲過程的分類

  • 系統存儲過程
  • 由系統定義,存放在master數據庫中
  • 類似C語言中的系統函數
  • 系統存儲過程的名稱都以“sp_”開頭或”xp_”開頭
  • 用戶自定義存儲過程
  • 由用戶在自己的數據庫中創建的存儲過程
  • 類似C語言中的用戶自定義函數

關於MySQL的存儲過程

存儲過程是數據庫存儲的一個重要的功能,但是MySQL5.0以前並不支持存儲過程,這使得MySQL在應用上大打折扣。好在MySQL 5.0終於開始已經支持存儲過程,這樣即可以大大提高數據庫的處理速度,同時也可以提高數據庫編程的靈活性。

一、MySQL存儲過程的創建 

 通過MySQL提供的Query browser來創建

1、首先我們通過Administrator在test數據庫中創建一個簡單的表名叫”products“的,裏面包括下面這兩個字段:

 
2、然後打開Query Browser,並且在test中創建存儲過程,存儲過程名稱爲”GetAll“。
 
 
3、可以看到系統會默認加入一些內容。這些內容當然可以不用理會,我們的存儲過程就是從BEGIN到END中間的那些。點擊Continue可 以把存儲進程加入到test數據庫的目錄中去。當然這裏系統提供的代碼也比較簡單,一個就是重新定義存儲過程代碼的分隔符爲”$$“,因爲默認的是”; “而分號卻是SQL的分隔符所以要重新定義一下分隔符,然後等存儲過程寫完後在把分隔符恢復爲”;“。接着分隔符定義的下一句就是如果當前存在同名的進 程,則進行刪除。在下面就是創建進程了。 
4、在begin和end之間 寫入一行select代碼
5、在右邊的窗口中雙擊GetAll這個存儲進程,並且執行,則得到下面這個結果

通過命令行創建

(1). 格式

MySQL存儲過程創建的格式:

CREATE PROCEDURE 過程名 ([過程參數[,...]])

[特性 ...] 過程體

這裏先舉個例子:

複製代碼
mysql> DELIMITER //  
mysql> CREATE PROCEDURE proc1(OUT s int)  
    -> BEGIN 
    -> SELECT COUNT(*) INTO s FROM user;  
    -> END 
    -> //  
mysql> DELIMITER ;
複製代碼

注:(1)這裏需要注意的是DELIMITER //DELIMITER ;兩句,DELIMITER是分割符的意思,因爲MySQL默認以";"爲分隔符,如果我們沒有聲明分割符,那麼編譯器會把存儲過程當成SQL語句進行處理,則存儲過程的編譯過程會報錯,所以要事先用DELIMITER關鍵字申明當前段分隔符,這樣MySQL纔會將";"當做存儲過程中的代碼,不會執行這些代碼,用完了之後要把分隔符還原。

2)存儲過程根據需要可能會有輸入、輸出、輸入輸出參數,這裏有一個輸出參數s,類型是int型,如果有多個參數用","分割開。

3)過程體的開始與結束使用BEGINEND進行標識。

(2). 聲明分割符

其實,關於聲明分割符,上面的註解已經寫得很清楚,不需要多說,只是稍微要注意一點的是:如果是用MySQLAdministrator管理工具時,可以直接創建,不再需要聲明。

(3). 參數

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

CREATE PROCEDURE([[IN |OUT |INOUT ] 參數名 數據類形...])

  • IN 輸入參數:表示該參數的值必須在調用存儲過程時指定,在存儲過程中修改該參數的值不能被返回,爲默認值
  • OUT 輸出參數:該值可在存儲過程內部被改變,並可返回
  • INOUT 輸入輸出參數:調用時指定,並且可被改變和返回

. IN參數例子

創建:

複製代碼
mysql > DELIMITER //  
mysql > CREATE PROCEDURE demo_in_parameter(IN p_in int)  
-> BEGIN   
-> SELECT p_in;   
-> SET p_in=2;   
-> SELECT p_in;   
-> END;   
-> //  
mysql > DELIMITER ; 
複製代碼

 執行結果:

複製代碼
 mysql > SET @p_in=1;  
mysql > CALL demo_in_parameter(@p_in);  
+------+  
| p_in |  
+------+  
|   1  |   
+------+  
+------+  
| p_in |  
+------+  
|   2  |   
+------+  
mysql> SELECT @p_in;  
+-------+  
| @p_in |  
+-------+  
|  1    |  
+-------+   
複製代碼

以上可以看出,p_in雖然在存儲過程中被修改,但並不影響@p_id的值

.OUT參數例子

創建:

複製代碼
mysql > DELIMITER //  
mysql > CREATE PROCEDURE demo_out_parameter(OUT p_out int)  
-> BEGIN 
-> SELECT p_out;  
-> SET p_out=2;  
-> SELECT p_out;  
-> END;  
-> //  
mysql > DELIMITER ; 
複製代碼

執行結果:

複製代碼
mysql > SET @p_out=1;  
mysql > CALL sp_demo_out_parameter(@p_out);  
+-------+  
| p_out |   
+-------+  
| NULL  |   
+-------+   
+-------+  
| p_out |  
+-------+  
|   2   |   
+-------+   
mysql> SELECT @p_out;  
+-------+  
| p_out |  
+-------+  
|   2   |  
+-------+  
複製代碼

. INOUT參數例子

創建:

複製代碼
mysql > DELIMITER //   
mysql > CREATE PROCEDURE demo_inout_parameter(INOUT p_inout int)   
-> BEGIN 
-> SELECT p_inout;  
-> SET p_inout=2;  
-> SELECT p_inout;   
-> END;  
-> //   
mysql > DELIMITER ; 
複製代碼

執行結果:

複製代碼
mysql > SET @p_inout=1;  
mysql > CALL demo_inout_parameter(@p_inout) ;  
+---------+  
| p_inout |  
+---------+  
|    1    |  
+---------+   
+---------+  
| p_inout |   
+---------+  
|    2    |  
+---------+   
mysql > SELECT @p_inout;  
+----------+  
| @p_inout |   
+----------+  
|    2     |  
+----------+ 
複製代碼

(4). 變量

. 變量定義

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

其中,datatypeMySQL的數據類型,如:int, float, date, varchar(length) 

例如:

DECLARE l_int int unsigned default 4000000;  
DECLARE l_numeric number(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'

. 變量賦值

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

. 用戶變量 

. MySQL客戶端使用用戶變量

複製代碼
 mysql > SELECT 'Hello World' into @x;  
mysql > SELECT @x;  
+-------------+  
|   @x        |  
+-------------+  
| Hello World |  
+-------------+  
mysql > SET @y='Goodbye Cruel World';  
mysql > SELECT @y;  
+---------------------+  
|     @y              |  
+---------------------+  
| Goodbye Cruel World |  
+---------------------+   
mysql > SET @z=1+2+3;  
mysql > SELECT @z;  
+------+  
| @z   |  
+------+  
|  6   |  
+------+ 
複製代碼

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

複製代碼
 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_proc);  
mysql> CALL p1( );  
mysql> CALL p2( );  
+-----------------------------------------------+  
| CONCAT('Last procedure was ',@last_proc  |  
+-----------------------------------------------+  
| Last procedure was p1                         |  
+-----------------------------------------------+  
複製代碼

注意:

用戶變量名一般以@開頭

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

(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 ;  
複製代碼

 

三.      MySQL存儲過程的調用

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

四.      MySQL存儲過程的查詢

我們像知道一個數據庫下面有那些表,我們一般採用show tables;進行查看。那麼我們要查看某個數據庫下面的存儲過程,是否也可以採用呢?答案是,我們可以查看某個數據庫下面的存儲過程,但是是令一鍾方式。

我們可以用

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

或者

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

或者

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

進行查詢。

如果我們想知道,某個存儲過程的詳細,那我們又該怎麼做呢?是不是也可以像操作表一樣用describe 表名進行查看呢?

答案是:我們可以查看存儲過程的詳細,但是需要用另一種方法:

SHOW CREATE PROCEDURE 數據庫.存儲過程名,就可以查看當前存儲過程的詳細。 

 

五.      MySQL存儲過程的修改

ALTER PROCEDURE

更改用CREATE PROCEDURE建立的預先指定的存儲過程,其不會影響相關存儲過程或存儲功能。 

 

六.      MySQL存儲過程的刪除

刪除一個存儲過程比較簡單,和刪除表一樣:

DROP PROCEDURE

MySQL的表格中刪除一個或多個存儲過程。 

 

      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 ;  
複製代碼

 (2). 條件語句

. if-then -else語句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql > DELIMITER // 
mysql > CREATE PROCEDURE proc2(IN parameter int
     -> begin
     -> declare var int
     -> set var=parameter+1; 
     -> if var=0 then
     -> insert into values(17); 
     -> end if; 
     -> if parameter=0 then
     -> update set s1=s1+1; 
     -> else
     -> update set s1=s1+2; 
     -> end if; 
     -> end
     -> // 
mysql > DELIMITER ;

. case語句: 

複製代碼
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 > DELIMITER ; 
複製代碼

(3). 循環語句

. while ···· end while

複製代碼
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

它在執行操作後檢查結果,而while則是執行前進行檢查。

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql > DELIMITER // 
mysql > CREATE PROCEDURE proc5 () 
     -> begin  
     -> declare int
     -> set v=0; 
     -> repeat 
     -> insert into values(v); 
     -> set v=v+1; 
     -> until v>=5 
     -> end repeat; 
     -> end
     -> // 
mysql > DELIMITER ;

. loop ·····end loop:

loop循環不需要初始條件,這點和while 循環相似,同時和repeat循環一樣不需要結束條件, 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:

通過引用複合語句的標號,來從新開始複合語句

複製代碼
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 ;  
複製代碼

八.      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 ]) //
strposition開始,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個空格

(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) //
開平方

  • 參考:http://blog.sina.com.cn/s/blog_52d20fbf0100ofd5.html
  •      http://www.2cto.com/database/201208/148791.html
  • 資料:http://tech.ccidnet.com/zt/guocheng/
  •      http://tech.ccidnet.com/zt/guocheng/
  •      http://www.cnblogs.com/hoojo/archive/2011/07/19/2110862.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章