1. 程式人生 > >mysql儲存過程詳細教程

mysql儲存過程詳細教程

原文

記錄MYSQL儲存過程中的關鍵語法:

DELIMITER // 宣告語句結束符,用於區分;
CEATE PROCEDURE demo_in_parameter(IN p_in int) 宣告儲存過程
BEGIN …. END 儲存過程開始和結束符號
SET @p_in=1 變數賦值
DECLARE l_int int unsigned default 4000000; 變數定義

什麼是mysql儲存例程?
儲存例程是儲存在資料庫伺服器中的一組sql語句,通過在查詢中呼叫一個指定的名稱來執行這些sql語句命令.

為什麼要使用mysql儲存過程?
我們都知道應用程式分為兩種,一種是基於web,一種是基於桌面,他們都和資料庫進行互動來完成資料的存取工作。假設現在有一種應用程式包含了這兩 種,現在要修改其中的一個查詢sql語句,那麼我們可能要同時修改他們中對應的查詢sql語句,當我們的應用程式很龐大很複雜的時候問題就出現這,不易維 護!另外把sql查詢語句放在我們的web程式或桌面中很容易遭到sql注入的破壞。而儲存例程正好可以幫我們解決這些問題。
儲存過程(stored procedure)、儲存例程(store routine)、儲存函式區別
Mysql儲存例程實際包含了儲存過程和儲存函式,它們被統稱為儲存例程。
其中儲存過程主要完成在獲取記錄或插入記錄或更新記錄或刪除記錄,即完成select insert delete update等的工作。而儲存函式只完成查詢的工作,可接受輸入引數並返回一個結果。

建立mysql儲存過程、儲存函式
create procedure 儲存過程名(引數)
儲存過程體
create function 儲存函式名(引數)

下面是儲存過程的例子:

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)過程體的開始與結束使用BEGIN與END進行標識。
這樣,我們的一個MySQL儲存過程就完成了,是不是很容易呢?看不懂也沒關係,接下來,我們詳細的講解。

引數

MySQL儲存過程的引數用在儲存過程的定義,共有三種引數型別,IN,OUT,INOUT,形式如:
CREATEPROCEDURE 儲存過程名([[IN |OUT |INOUT ] 引數名 資料類形…])
IN 輸入引數:表示該引數的值必須在呼叫儲存過程時指定,在儲存過程中修改該引數的值不能被返回,為預設值
OUT 輸出引數:該值可在儲存過程內部被改變,並可返回
INOUT 輸入輸出引數:呼叫時指定,並且可被改變和返回

. IN引數例子

建立:

1.  mysql > DELIMITER //  
2.  mysql > CREATE PROCEDURE
demo_in_parameter(IN p_in int) 3. -> BEGIN 4. -> SELECT p_in;
5. -> SET p_in=2; 6. -> SELECT p_in; 7. -> END; 8. -> // 9. mysql > DELIMITER ;

執行結果:

1.  mysql > SET @p_in=1;  
2.  mysql > CALL demo_in_parameter(@p_in);  
3.  +------+  
4.  | p_in |  
5.  +------+  
6.  |   1  |   
7.  +------+  
8.   
9.  +------+  
10.| p_in |  
11.+------+  
12.|   2  |   
13.+------+  
14. 
15.mysql> SELECT @p_in;  
16.+-------+  
17.| @p_in |  
18.+-------+  
19.|  1    |  
20.+-------+  

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

.OUT引數例子

建立:

1.  mysql > DELIMITER //  
2.  mysql > CREATE PROCEDURE demo_out_parameter(OUT p_out int)  
3.       -> BEGIN 
4.       -> SELECT p_out;  
5.       -> SET p_out=2;  
6.       -> SELECT p_out;  
7.       -> END;  
8.       -> //  
9.  mysql > DELIMITER ; 

執行結果:

1.  mysql > SET @p_out=1;  
2.  mysql > CALL sp_demo_out_parameter(@p_out);  
3.  +-------+  
4.  | p_out |   
5.  +-------+  
6.  | NULL  |   
7.  +-------+  
8.   
9.  +-------+  
10.| p_out |  
11.+-------+  
12.|   2   |   
13.+-------+  
14. 
15.mysql> SELECT @p_out;  
16.+-------+  
17.| p_out |  
18.+-------+  
19.|   2   |  
20.+-------+  

INOUT引數例子

建立:

1.  mysql > DELIMITER //   
2.  mysql > CREATE PROCEDURE demo_inout_parameter(INOUT p_inout int)   
3.       -> BEGIN 
4.       -> SELECT p_inout;  
5.       -> SET p_inout=2;  
6.       -> SELECT p_inout;   
7.       -> END;  
8.       -> //   
9.  mysql > DELIMITER ; 

執行結果:

1.  mysql > SET @p_inout=1;  
2.  mysql > CALL demo_inout_parameter(@p_inout) ;  
3.  +---------+  
4.  | p_inout |  
5.  +---------+  
6.  |    1    |  
7.  +---------+  
8.   
9.  +---------+  
10.| p_inout |   
11.+---------+  
12.|    2    |  
13.+---------+  
14. 
15.mysql > SELECT @p_inout;  
16.+----------+  
17.| @p_inout |   
18.+----------+  
19.|    2     |  
20.+----------+ 

區域性變數

變數定義

區域性變數宣告一定要放在儲存過程體的開始
DECLAREvariable_name [,variable_name…] datatype [DEFAULT value];
其中,datatype為MySQL的資料型別,如:int, float, date,varchar(length)
例如:

1.  DECLARE l_int int unsigned default 4000000;  
2.  DECLARE l_numeric number(8,2) DEFAULT 9.95;  
3.  DECLARE l_date date DEFAULT '1999-12-31';  
4.  DECLARE l_datetime datetime DEFAULT '1999-12-31 23:59:59';  
5.  DECLARE l_varchar varchar(255) DEFAULT 'This will not be padded';   

變數賦值

SET 變數名 = 表示式值 [,variable_name = expression …]

使用者變數

在MySQL客戶端使用使用者變數

1.  mysql > SELECT 'Hello World' into @x;  
2.  mysql > SELECT @x;  
3.  +-------------+  
4.  |   @x        |  
5.  +-------------+  
6.  | Hello World |  
7.  +-------------+  
8.  mysql > SET @y='Goodbye Cruel World';  
9.  mysql > SELECT @y;  
10.+---------------------+  
11.|     @y              |  
12.+---------------------+  
13.| Goodbye Cruel World |  
14.+---------------------+  
15. 
16.mysql > SET @z=1+2+3;  
17.mysql > SELECT @z;  
18.+------+  
19.| @z   |  
20.+------+  
21.|  6   |  
22.+------+  

在儲存過程中使用使用者變數

1.  mysql > CREATE PROCEDURE GreetWorld( ) SELECT CONCAT(@greeting,' World');  
2.  mysql > SET @greeting='Hello';  
3.  mysql > CALL GreetWorld( );  
4.  +----------------------------+  
5.  | CONCAT(@greeting,' World') |  
6.  +----------------------------+  
7.  |  Hello World               |  
8.  +----------------------------+  

在儲存過程間傳遞全域性範圍的使用者變數

1.  mysql> CREATE PROCEDURE p1()   SET @last_procedure='p1';  
2.  mysql> CREATE PROCEDURE p2() SELECT CONCAT('Last procedure was ',@last_procedure);  
3.  mysql> CALL p1( );  
4.  mysql> CALL p2( );  
5.  +-----------------------------------------------+  
6.  | CONCAT('Last procedure was ',@last_proc       |  
7.  +-----------------------------------------------+  
8.  | Last procedure was p1                         |  
9.  +-----------------------------------------------+  

注意:
①使用者變數名一般以@開頭
②濫用使用者變數會導致程式難以理解及管理

註釋

MySQL儲存過程可使用兩種風格的註釋
雙模槓:–
該風格一般用於單行註釋
c風格: 一般用於多行註釋
例如:

1.  mysql > DELIMITER //  
2.  mysql > CREATE PROCEDURE proc1 --name儲存過程名  
3.       -> (IN parameter1 INTEGER)   
4.       -> BEGIN   
5.       -> DECLARE variable1 CHAR(10);   
6.       -> IF parameter1 = 17 THEN   
7.       -> SET variable1 = 'birds';   
8.       -> ELSE 
9.       -> SET variable1 = 'beasts';   
10.     -> END IF;   
11.     -> INSERT INTO table1 VALUES (variable1);  
12.     -> END   
13.     -> //  
14.mysql > DELIMITER ;  

MySQL儲存過程的呼叫

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

MySQL儲存過程的查詢

我們像知道一個數據庫下面有那些表,我們一般採用showtables;進行檢視。那麼我們要檢視某個資料庫下面的儲存過程,是否也可以採用呢?答案是,我們可以檢視某個資料庫下面的儲存過程,但是是令一鍾方式。
我們可以用
selectname from mysql.proc where db=’資料庫名’;
或者
selectroutine_name from information_schema.routines where routine_schema=’資料庫名’;
或者
showprocedure status where db=’資料庫名’;
進行查詢。
如果我們想知道,某個儲存過程的詳細,那我們又該怎麼做呢?是不是也可以像操作表一樣用describe 表名進行檢視呢?
答案是:我們可以檢視儲存過程的詳細,但是需要用另一種方法:
SHOWCREATE PROCEDURE 資料庫.儲存過程名;
就可以檢視當前儲存過程的詳細。

MySQL儲存過程的修改

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

MySQL儲存過程的刪除

刪除一個儲存過程比較簡單,和刪除表一樣:
DROPPROCEDURE
從MySQL的表格中刪除一個或多個儲存過程。

MySQL儲存過程的控制語句

變數作用域

內部的變數在其作用域範圍內享有更高的優先權,當執行到end。變數時,內部變數消失,此時已經在其作用域外,變數不再可見了,應為在儲存
過程外再也不能找到這個申明的變數,但是你可以通過out引數或者將其值指派
給會話變數來儲存其值。

1.  mysql > DELIMITER //  
2.  mysql > CREATE PROCEDURE proc3()  
3.       -> begin 
4.       -> declare x1 varchar(5) default 'outer';  
5.       -> begin 
6.       -> declare x1 varchar(5) default 'inner';  
7.       -> select x1;  
8.       -> end;  
9.       -> select x1;  
10.     -> end;  
11.     -> //  
12.mysql > DELIMITER ;  

條件語句

if-then -else語句

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

case語句:

1.  mysql > DELIMITER //  
2.  mysql > CREATE PROCEDURE proc3 (in parameter int)  
3.       -> begin 
4.       -> declare var int;  
5.       -> set var=parameter+1;  
6.       -> case var  
7.       -> when 0 then   
8.       -> insert into t values(17);  
9.       -> when 1 then   
10.     -> insert into t values(18);  
11.     -> else   
12.     -> insert into t values(19);  
13.     -> end case;  
14.     -> end;  
15.     -> //  
16.mysql > DELIMITER ; 
case
        when var=0 then
               insert into t values(30);
        when var>0 then
        when var<0 then
        else

end case

迴圈語句

while ···· end while:

1.  mysql > DELIMITER //  
2.  mysql > CREATE PROCEDURE proc4()  
3.       -> begin 
4.       -> declare var int;  
5.       -> set var=0;  
6.       -> while var<6 do  
7.       -> insert into t values(var);  
8.       -> set var=var+1;  
9.       -> end while;  
10.     -> end;  
11.     -> //  
12.mysql > DELIMITER ; 
 while條件 do
--迴圈體
endwhile

repeat···· end repeat:

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

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

loop ·····endloop:

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

1.  mysql > DELIMITER //  
2.  mysql > CREATE PROCEDURE proc6 ()  
3.       -> begin 
4.       -> declare v int;  
5.       -> set v=0;  
6.       -> LOOP_LABLE:loop  
7.       -> insert into t values(v);  
8.       -> set v=v+1;  
9.       -> if v >=5 then 
10.     -> leave LOOP_LABLE;  
11.     -> end if;  
12.     -> end loop;  
13.     -> end;  
14.     -> //  
15.mysql > DELIMITER ;  

LABLES 標號:

標號可以用在begin repeat while 或者loop 語句前,語句標號只能在合法的語句前面使用。可以跳出迴圈,使執行指令達到複合語句的最後一步。

ITERATE迭代

ITERATE:

1.    通過引用複合語句的標號,來從新開始複合語句
2.  mysql > DELIMITER //  
3.  mysql > CREATE PROCEDURE proc10 ()  
4.       -> begin 
5.       -> declare v int;  
6.       -> set v=0;  
7.       -> LOOP_LABLE:loop  
8.      -> if v=3 then   
9.      -> set v=v+1;  
10.        -> ITERATE LOOP_LABLE;  
11.        -> end if;  
12.     -> insert into t values(v);  
13.     -> set v=v+1;  
14.        -> if v>=5 then 
15.        -> leave LOOP_LABLE;  
16.     -> end if;  
17.     -> end loop;  
18.     -> end;  
19.     -> //  
20.mysql > DELIMITER ; 

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 
1.  mysql> select substring('abcd',0,2);  
2.  +-----------------------+  
3.  | substring('abcd',0,2) |  
4.  +-----------------------+  
5.  |                       |  
6.  +-----------------------+  
7.  1 row in set (0.00 sec)  
8.   
9.  mysql> select substring('abcd',1,2);  
10.+-----------------------+  
11.| substring('abcd',1,2) |  
12.+-----------------------+  
13.|     ab                |  
14.+-----------------------+  
15.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)預設變為整形值
1.  mysql> select round(1.23);  
2.  +-------------+  
3.  | round(1.23) |  
4.  +-------------+  
5.  |           1 |  
6.  +-------------+  
7.  1 row in set (0.00 sec)  
8.   
9.  mysql> select round(1.56);  
10.+-------------+  
11.| round(1.56) |  
12.+-------------+  
13.|           2 |  
14.+-------------+  
15.1 row in set (0.00 sec) 


(2)可以設定小數位數,返回浮點型資料
1.  mysql> select round(1.567,2);  
2.  +----------------+  
3.  | round(1.567,2) |  
4.  +----------------+  
5.  |           1.57 |  
6.  +----------------+  
7.  1 row in set (0.00 sec) 
SIGN (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) //分返回符號,正負或0
SQRT(number2) //開平方

MySql分頁儲存過程

MySql測試版本:5.0.41-community-nt

DROP PROCEDURE IF EXISTS pr_pager;
CREATE PROCEDURE pr_pager(

    IN    p_table_name        VARCHAR(1024),        
    IN    p_fields            VARCHAR(1024),        
    IN    p_page_size            INT,                
    IN    p_page_now            INT,                
    IN    p_order_string        VARCHAR(128),        
    IN    p_where_string        VARCHAR(1024),        
     OUT    p_out_rows            INT                    

)
    NOT DETERMINISTIC
     SQL SECURITY DEFINER
     COMMENT '分頁儲存過程'

BEGIN


    DECLARE m_begin_row INT DEFAULT 0;
    DECLARE m_limit_string CHAR(64);


    SET m_begin_row = (p_page_now - 1) * p_page_size;
    SET m_limit_string = CONCAT(' LIMIT ', m_begin_row, ', ', p_page_size);

    SET @COUNT_STRING = CONCAT('SELECT COUNT(*) INTO @ROWS_TOTAL FROM ', p_table_name, ' ', p_where_string);
    SET @MAIN_STRING = CONCAT('SELECT ', p_fields, ' FROM ', p_table_name, ' ', p_where_string, ' ', p_order_string,m_limit_string);


    PREPARE count_stmt FROM @COUNT_STRING;
    EXECUTE count_stmt;
    DEALLOCATE PREPARE count_stmt;
    SET p_out_rows = @ROWS_TOTAL;

    PREPARE main_stmt FROM @MAIN_STRING;
    EXECUTE main_stmt;
    DEALLOCATE PREPARE main_stmt;

END;

呼叫

mysql> call pr_pager("t","var",3,3,"","",@result);
mysql> call pr_pager("t","var",3,2,"","",@result);