1. 程式人生 > >儲存過程(mysql)

儲存過程(mysql)

mysql儲存過程詳解
我們現在MySql中建立一個bjpowernode的資料庫,然後倒入備份表

  1. mysql> create database bjpowernode;
  2. Query OK, 1 row affected (0.00 sec)
  3. mysql> use bjpowernode;
  4. Database changed
  5. mysql> source d:/bjpowernode.sql;
  6. Query OK, 0 rows affected, 1 warning (0.00 sec)
  7. Query OK, 0 rows affected, 1 warning (0.00 sec)
  8. Query OK, 0 rows affected, 1 warning (0.02 sec)
  9. Query OK, 0 rows affected (0.08 sec)
  10. Query OK, 0 rows affected (0.05 sec)
  11. Query OK, 0 rows affected (0.05 sec)
  12. Query OK, 1 row affected (0.03 sec)

查詢資料庫中的表,會顯示三個表,是我們剛剛匯入的備份表

  1. mysql> show tables;

  2. ±----------------------+

  3. | Tables_in_bjpowernode |

  4. ±----------------------+

  5. | dept |

  6. | emp |

  7. | salgrade |

  8. ±----------------------+

  9. 3 rows in set (0.00 sec)

  10.  儲存過程簡介
    

我們常用的操作資料庫語言SQL語句在執行的時候需要要先編譯,然後執行,而儲存過程(Stored Procedure)是一組為了完成特定功能的SQL語句集,經編譯後儲存在資料庫中,使用者通過指定儲存過程的名字並給定引數(如果該儲存過程帶有引數)來呼叫執行它。
一個儲存過程是一個可程式設計的函式,它在資料庫中建立並儲存。它可以有SQL語句和一些特殊的控制結構組成。當希望在不同的應用程式或平臺上執行相同的函式,或者封裝特定功能時,儲存過程是非常有用的。資料庫中的儲存過程可以看做是對程式設計中面向物件方法的模擬。它允許控制資料的訪問方式。
儲存過程通常有以下優點:
(1).儲存過程增強了SQL語言的功能和靈活性。儲存過程可以用流控制語句編寫,有很強的靈活性,可以完成複雜的判斷和較複雜的運算。
(2).儲存過程允許標準組件是程式設計。儲存過程被建立後,可以在程式中被多次呼叫,而不必重新編寫該儲存過程的SQL語句。而且資料庫專業人員可以隨時對儲存過程進行修改,對應用程式原始碼毫無影響。
(3).儲存過程能實現較快的執行速度。如果某一操作包含大量的Transaction-SQL程式碼或分別被多次執行,那麼儲存過程要比批處理的執行速度快很多。因為儲存過程是預編譯的。在首次執行一個儲存過程時查詢,優化器對其進行分析優化,並且給出最終被儲存在系統表中的執行計劃。而批處理的Transaction-SQL語句在每次執行時都要進行編譯和優化,速度相對要慢一些。
(4).儲存過程能過減少網路流量。針對同一個資料庫物件的操作(如查詢、修改),如果這一操作所涉及的Transaction-SQL語句被組織程儲存過程,那麼當在客戶計算機上呼叫該儲存過程時,網路中傳送的只是該呼叫語句,從而大大增加了網路流量並降低了網路負載。
(5).儲存過程可被作為一種安全機制來充分利用。系統管理員通過執行某一儲存過程的許可權進行限制,能夠實現對相應的資料的訪問許可權的限制,避免了非授權使用者對資料的訪問,保證了資料的安全。

  1.  關於MySQL的儲存過程
    

儲存過程是資料庫儲存的一個重要的功能,但是MySQL在5.0以前並不支援儲存過程,這使得MySQL在應用上大打折扣。好在MySQL 5.0終於開始已經支援儲存過程,這樣即可以大大提高資料庫的處理速度,同時也可以提高資料庫程式設計的靈活性。
3. MySQL儲存過程的建立

(1). 格式
MySQL儲存過程建立的格式:CREATE PROCEDURE 過程名 ([過程引數[,…]])
[特性 …] 過程體

這裡先舉個例子:

  1. mysql> DELIMITER //
  2. mysql> CREATE PROCEDURE proc1(OUT s int)
  3.  -> BEGIN 
    
  4.  -> SELECT COUNT(*) INTO s FROM user;  
    
  5.  -> END ; 
    
  6.  -> //  
    
  7. Query OK, 0 rows affected (0.01 sec)
  8. mysql> DELIMITER ;

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

例如:MySql預設的情況下以分號作為語句的結束,輸入SQL語句,在末尾加上分號,這樣MySql會執行這行程式碼
我們可以改變MySql的分隔符,把分號改成 // ,輸入語句
mysql> delimiter //

這時候MySql的結束語句分隔符就改變成了// ,原來的分號就不起作用了
在結束語句的時候需要加上 //,這樣SQL語句才會執行

  1. mysql> select * from emp//
  2. ±------±-------±----------±-----±-----------±--------±--------±-------+
  3. | EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
  4. ±------±-------±----------±-----±-----------±--------±--------±-------+
  5. | 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
  6. | 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
  7. | 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
  8. | 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
  9. | 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
  10. | 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
  11. | 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
  12. | 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
  13. | 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
  14. | 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
  15. | 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
  16. | 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
  17. | 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
  18. | 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
  19. ±------±-------±----------±-----±-----------±--------±--------±-------+
  20. 14 rows in set (0.00 sec)

最後,我們把MySql的結束分隔符恢復成 ; ,這樣的話,在結束語句的末尾還是用分號結束,SQL語句才會被執行

  1. mysql> delimiter ;
  2. mysql> select * from emp;
  3. ±------±-------±----------±-----±-----------±--------±--------±-------+
  4. | EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
  5. ±------±-------±----------±-----±-----------±--------±--------±-------+
  6. | 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
  7. | 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
  8. | 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
  9. | 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
  10. | 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
  11. | 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
  12. | 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
  13. | 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
  14. | 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
  15. | 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
  16. | 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
  17. | 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
  18. | 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
  19. | 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
  20. ±------±-------±----------±-----±-----------±--------±--------±-------+
  21. 14 rows in set (0.00 sec)

(2)儲存過程根據需要可能會有輸入、輸出、輸入輸出引數,這裡有一個輸出引數s,型別是int型,如果有多個引數用","分割開。
(3)過程體的開始與結束使用BEGIN與END進行標識。
這樣,我們的一個MySQL儲存過程就完成了,是不是很容易呢?看不懂也沒關係,接下來,我們詳細的講解。

(2). 宣告分割符

其實,關於宣告分割符,上面的註解已經寫得很清楚,不需要多說,只是稍微要注意一點的是:如果是用MySQL的Administrator管理工具時,例如NaviCat for MySql,可以直接建立,不再需要宣告。

NaviCat for MySql工具的使用:我們下載的是綠色版,解壓縮之後,輸入註冊碼就可直接使用,非常方便,使用介面如下:

我們需要建立和資料庫伺服器的連線.點選 檔案->新建連線,填入資料庫的相關資訊之後,點選確定按鈕.

建立好的連線如下,雙擊本機,開啟連線,我們可以直接看到MySql中的資料庫
雙擊我們要使用的資料庫,然後點選查詢->新建查詢,彈出視窗,我們可以在這個建立中編輯SQL語句,非常方便

在編輯視窗中編輯SQL語句,然後選中該語句,點選右鍵->執行已選擇的,即可執行選中的SQL語句,並且顯示查詢結果

在NaviCat中編輯儲存過程非常方便,不需要在宣告分隔符,直接編輯程式碼,然後選中程式碼 ,即可執行,是不是被命令列客戶端爽多了_

(3). 引數
MySQL儲存過程的引數用在儲存過程的定義,共有三種引數型別,IN,OUT,INOUT,形式如:
CREATE PROCEDURE([[IN |OUT |INOUT ] 引數名 資料類形…])
IN 輸入引數:表示該引數的值必須在呼叫儲存過程時指定,在儲存過程中修改該引數的值不能被返回,為預設值
OUT 輸出引數:該值可在儲存過程內部被改變,並可返回
INOUT 輸入輸出引數:呼叫時指定,並且可被改變和返回
Ⅰ. IN引數例子
在NaviCat中建立儲存過程:
CREATE PROCEDURE PROC2(P_IN INT)
BEGIN
SELECT P_IN; – 查詢P_IN引數的值
SET P_IN = 3; – 設定P_IN引數的值
SELECT P_IN ; – 查詢P_IN引數的值
END;

語句執行的過程如下圖:

呼叫儲存過程,查詢結果
SET @P_IN = 10; – 設定@P_IN =10
CALL PROC2(@P_IN); – 呼叫儲存過程,並且傳遞@P_IN的引數
SELECT @P_IN; – 查詢@P_IN的結果

語句的執行過程如下圖所示

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

Ⅱ.OUT引數例子
建立:
CREATE PROCEDURE PROC3(OUT P_OUT INT)
BEGIN
SELECT P_OUT;
SET P_OUT = 10;
SELECT P_OUT;
END;

執行過程如下:

呼叫儲存過程,查詢結果
SET @P_OUT = 1000; – 設定@P_OUT變數為1000
CALL PROC3(@P_OUT); – 呼叫儲存過程並且傳遞@P_OUT引數
SELECT @P_OUT; – 查詢P_OUT的結構

執行結果如下:

Ⅲ. INOUT引數例子
建立:
– 注意:INOUT之間沒有空格
CREATE PROCEDURE PROC4(INOUT P_IN_OUT INT) BEGIN
SELECT P_IN_OUT;
SET P_IN_OUT = 10;
SELECT P_IN_OUT;
END;

執行結果如下:

呼叫儲存過程:
SET @P_IN_OUT = 50;
CALL PROC3(@P_IN_OUT);
SELECT @P_IN_OUT;

執行結果如下:

(4). 變數
Ⅰ. 變數定義
DECLARE variable_name [,variable_name…] datatype [DEFAULT value];
其中,datatype為MySQL的資料型別,如:int, float, date, varchar(length)
例如:
CREATE PROCEDURE PROC4()
BEGIN
– 宣告變數.注意:宣告變數的語句只能寫在BEGIN END之間
DECLARE SCHOOL VARCHAR(100) DEFAULT “動力節點”;
DECLARE L_INT INT DEFAULT 40;
DECLARE L_NUMERIC DOUBLE(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’;

SELECT SCHOOL ;-- 查詢變數的值
SET SCHOOL = “動力節點北京北部校區”,L_INT =100;-- 給變數重新賦值
SELECT SCHOOL;-- 再次查詢變數
END;

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

Ⅲ. 使用者變數

ⅰ. 在MySQL客戶端使用使用者變數
SELECT “Hello World” INTO @S;
SELECT @S;

SET @S = “Hello World”;
SELECT @S;

SET @RES = 1+2+3;
SELECT @RES;

ⅱ. 在儲存過程中使用使用者變數
– 宣告儲存過程
CREATE PROCEDURE PROC5()
BEGIN
SELECT CONCAT(@STR1,“World”);
END;

– 首先定義一個變數,然後呼叫儲存過程
SET @STR1 = “Hello”;
CALL PROC5();

執行效果如下

ⅲ. 在儲存過程間傳遞全域性範圍的使用者變數
– 宣告儲存過程P1,在P1中定義變數@LAST_PROC;
CREATE PROCEDURE P1()
BEGIN
SET @LAST_PROC = “P1”;
END;

– 宣告儲存過程P2,在P2中使用P1中定義的變數@LAST_PROC
CREATE PROCEDURE P2()
BEGIN
SELECT CONCAT(“最後一個儲存過程是”,@LAST_PROC);
END;

– 呼叫儲存過程P1(),P2();
CALL P1();
CALL P2();

執行結果如下:

注意:
使用者變數名一般以@開頭

(5). 註釋

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

  1.  MySQL儲存過程的呼叫
    

用call和你過程名以及一個括號,括號裡面根據需要,加入引數,引數包括輸入引數、輸出引數、輸入輸出引數。具體的呼叫方法可以參看上面的例子。
5. MySQL儲存過程的查詢
我們像知道一個數據庫下面有那些表,我們一般採用show tables;進行檢視。那麼我們要檢視某個資料庫下面的儲存過程,是否也可以採用呢?答案是,我們可以檢視某個資料庫下面的儲存過程,但是是令一鍾方式。
show procedure status where db=‘資料庫名’;

具體語句:
SHOW PROCEDURE STATUS WHERE DB=“bjpowernode”;

執行結果如下:

進行查詢。
如果我們想知道,某個儲存過程的詳細,那我們又該怎麼做呢?是不是也可以像操作表一樣用desc 表名進行檢視呢?
答案是:我們可以檢視儲存過程的詳細,但是需要用另一種方法:
SHOW CREATE PROCEDURE 資料庫.儲存過程名;
就可以檢視當前儲存過程的詳細。

具體語句如下:
SHOW CREATE PROCEDURE BJPOWERNODE.P1;

執行結果如下:

  1.  MySQL儲存過程的修改
    

目前,MySQL還不提供對已存在的儲存過程的程式碼修改 如果,一定要修改儲存過程的程式碼,必須,先將儲存過程刪除之後,在重新編寫程式碼,或者建立一個新的儲存過程

  1.  MySQL儲存過程的刪除
    

刪除一個儲存過程比較簡單,和刪除表一樣:
DROP PROCEDURE [IF EXISTS] 儲存過程名稱

從MySQL的表格中刪除一個或多個儲存過程。
DROP PROCEDURE IF EXISTS P1;
DROP PROCEDURE IF EXISTS P2;

  1.  MySQL儲存過程的控制語句
    

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

– 建立儲存過程
CREATE PROCEDURE PROC6()
BEGIN
DECLARE X1 VARCHAR(5) DEFAULT “OUTER”;
– ----------巢狀塊-------------------------
BEGIN
DECLARE X1 VARCHAR(5) DEFAULT “INNER”;
SELECT X1; – 結果是"INNER"
END;


SELECT X1; – 結果是"OUTER"
END;

– 呼叫儲存過程
CALL PROC6();

執行結果如下:

(2). 條件語句
Ⅰ. if-then -else語句
建立一個表
CREATE TABLE TEMP (ID INT(4));

建立儲存過程
CREATE PROCEDURE PROC7(IN PARAM INT)
BEGIN
DECLARE VAR INT;
SET VAR = PARAM + 1;
– ----------IF語句---------------
– 注意:判斷var是否等於1,要使用=,而不是==;
IF VAR = 1 THEN
INSERT INTO TEMP VALUES (100);
END IF;
– ----------IF ELSE語句---------------
IF PARAM = 0 THEN
UPDATE TEMP SET ID = ID + 1;
ELSE
UPDATE TEMP SET ID = ID + 2;
END IF;
END;

呼叫儲存過程
CALL PROC7(0);

查詢結果:
SELECT * FROM TEMP;

語句執行結果如下:

Ⅱ. case語句:
清空TEMP表中的記錄
DELETE FROM TEMP;

建立儲存過程
CREATE PROCEDURE PROC8(IN PARAM INT)
BEGIN
DECLARE VAR INT;
SET VAR = PARAM + 1;

CASE VAR
WHEN 1 THEN
INSERT INTO TEMP VALUES (1);
WHEN 2 THEN
INSERT INTO TEMP VALUES (2);
ELSE
INSERT INTO TEMP VALUES (100);
END CASE;
END;

呼叫儲存過程
CALL PROC8(10);

查詢結果:
SELECT * FROM TEMP;

語句執行結果如下

(3). 迴圈語句
Ⅰ. while •••• end while:

清空TEMP表中的記錄
DELETE FROM TEMP;

建立儲存過程
CREATE PROCEDURE PROC9()
BEGIN
DECLARE VAR INT DEFAULT 0;
WHILE VAR < 10 DO
INSERT INTO TEMP VALUES (VAR);
SET VAR = VAR + 1;
END WHILE;
END;

呼叫儲存過程
CALL PROC9();

查詢結果:
SELECT * FROM TEMP;

語句執行結果如下:

Ⅱ. repeat•••• end repeat:
它在執行操作後檢查結果,而while則是執行前進行檢查。

清空TEMP表中的記錄
DELETE FROM TEMP;

建立儲存過程
CREATE PROCEDURE PROC10()
BEGIN
DECLARE VAR INT DEFAULT 0;
REPEAT
INSERT INTO TEMP VALUES (VAR);
SET VAR = VAR + 1;
UNTIL VAR >= 10 – 末尾沒有分號
END REPEAT;
END;

呼叫儲存過程
CALL PROC10();

查詢結果:
SELECT * FROM TEMP;

語句執行結果:

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

建立儲存過程
CREATE PROCEDURE PROC11()
BEGIN
DECLARE VAR INT DEFAULT 0;
LOOP1:LOOP
INSERT INTO TEMP VALUES (VAR);
SET VAR = VAR + 1;
IF VAR > 10 THEN – 迴圈終止條件
LEAVE LOOP1;
END IF;
END LOOP;
END;

CALL PROC11(); – 呼叫儲存過程
SELECT * FROM TEMP; – 查詢結果

語句執行結果如下: