程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> 關於MYSQL數據庫 >> MySQL5.0新特性教程 存儲過程:第三講

MySQL5.0新特性教程 存儲過程:第三講

編輯:關於MYSQL數據庫

The New SQL Statements 新SQL語句

Variables 變量

  在復合語句中聲明變量的指令是DECLARE。

  (1) Example with two DECLARE statements

  兩個DECLARE語句的例子



WHILE ... END WHILE
CREATE PROCEDURE p8 ()

BEGIN

DECLARE a INT;

DECLARE b INT;

SET a = 5;

SET b = 5;

INSERT INTO t VALUES (a);

SELECT s1 * a FROM t WHERE s1 >= b;

END; // /* I won't CALL this */

  在過程中定義的變量並不是真正的定義,你只是在BEGIN/END塊內定義了而已(譯注:也就是形參)。

 注意這些變量和會話變量不一樣,不能使用修飾符@你必須清楚的在BEGIN/END塊中聲明變量和它們的類型。

  變量一旦聲明,你就能在任何能使用會話變量、文字、列名的地方使用。

  (2) Example with no DEFAULT clause and SET statement

  沒有默認子句和設定語句的例子

CREATE PROCEDURE p9 ()

BEGIN

DECLARE a INT /* there is no DEFAULT clause */;

DECLARE b INT /* there is no DEFAULT clause */;

SET a = 5; /* there is a SET statement */

SET b = 5; /* there is a SET statement */

INSERT INTO t VALUES (a);

SELECT s1 * a FROM t WHERE s1 >= b;

END; // /* I won't CALL this */

  有很多初始化變量的方法。如果沒有默認的子句,那麼變量的初始值為NULL。你可以在任何時候使用SET語句給變量賦值。

  (3) Example with DEFAULT clause

  含有DEFAULT子句的例子

CREATE PROCEDURE p10 ()

BEGIN

DECLARE a, b INT DEFAULT 5;

INSERT INTO t VALUES (a);

SELECT s1 * a FROM t WHERE s1 >= b;

END; //

  我們在這裡做了一些改變,但是結果還是一樣的。在這裡使用了DEFAULT子句來設定初始值,這就不需要把DECLARE和SET語句的實現分開了。

  (4) Example of CALL

  調用的例子

MySQL> CALL p10() //

+--------+

| s1 * a |

+--------+

| 25 |

| 25 |

+--------+

2 rows in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

  結果顯示了過程能正常工作

  (5) Scope

  作用域

CREATE PROCEDURE p11 ()

BEGIN

DECLARE x1 CHAR(5) DEFAULT 'outer';

BEGIN

DECLARE x1 CHAR(5) DEFAULT 'inner';

SELECT x1;

END;

SELECT x1;

END; //

  現在我們來討論一下作用域的問題。例子中有嵌套的BEGIN/END塊,當然這是合法的。同時包含兩個變量,名字都是x1,這樣也是合法的。內部的變量在其作用域內享有更高的優先權。當執行到END語句時,內部變量消失,此時已經在其作用域外,變量不再可見了,因此在存儲過程外再也不能找到這個聲明了的變量,但是你可以通過OUT參數或者將其值指派 給會話變量來保存其值。


  調用作用域例子的過程:

MySQL> CALL p11()//

+-------+

| x1 |

+-------+

| inner |

+-------+

+-------+

| x1 |

+-------+

| outer |

+-------+

  我們看到的結果時第一個SELECT語句檢索到最內層的變量,第二個檢索到第二層的變量

Conditions and IF-THEN-ELSE 條件式和IF-THEN-ELSE

  1.

  現在我們可以寫一些包含條件式的例子:

CREATE PROCEDURE p12 (IN parameter1 INT)

BEGIN

DECLARE variable1 INT;

SET variable1 = parameter1 + 1;

IF variable1 = 0 THEN

INSERT INTO t VALUES (17);

END IF;

IF parameter1 = 0 THEN

UPDATE t SET s1 = s1 + 1;

ELSE

UPDATE t SET s1 = s1 + 2;

END IF;

END; //

  這裡是一個包含IF語句的過程。裡面有兩個IF語句,一個是IF語句END IF,另一個是IF語句ELSE語句END IF。我們可以在這裡使用復雜的過程,但我會盡量使其簡單讓你能更容易弄清楚。

  2.

CALL p12 (0) //

  我們調用這個過程,傳入值為0,這樣parameter1的值將為0。

 3.

CREATE PROCEDURE p12 (IN parameter1 INT)

BEGIN

DECLARE variable1 INT;

SET variable1 = parameter1 + 1; <--

IF variable1 = 0 THEN

INSERT INTO t VALUES (17);

END IF;

IF parameter1 = 0 THEN

UPDATE t SET s1 = s1 + 1;

ELSE

UPDATE t SET s1 = s1 + 2;

END IF;

END; //

  這裡變量variable1被賦值為parameter1加1的值,所以執行後變量variable1為1。

  4.

CREATE PROCEDURE p12 (IN parameter1 INT)

BEGIN

DECLARE variable1 INT;

SET variable1 = parameter1 + 1;

IF variable1 = 0 THEN <--

INSERT INTO t VALUES (17);

END IF;

IF parameter1 = 0 THEN

UPDATE t SET s1 = s1 + 1;

ELSE

UPDATE t SET s1 = s1 + 2;

END IF;

END; //

  因為變量variable1值為1,因此條件"if variable1 = 0"為假,

IF

……

END IF

  被跳過,沒有被執行。

  5.

CREATE PROCEDURE p12 (IN parameter1 INT)

BEGIN

DECLARE variable1 INT;

SET variable1 = parameter1 + 1;

IF variable1 = 0 THEN

INSERT INTO t VALUES (17);

END IF;

IF parameter1 = 0 THEN <--

UPDATE t SET s1 = s1 + 1;

ELSE

UPDATE t SET s1 = s1 + 2;

END IF;

END; //

  到第二個IF條件,判斷結果為真,於是中間語句被執行了

  6.

CREATE PROCEDURE p12 (IN parameter1 INT)

BEGIN

DECLARE variable1 INT;

SET variable1 = parameter1 + 1;

IF variable1 = 0 THEN

INSERT INTO t VALUES (17);

END IF;

IF parameter1 = 0 THEN

UPDATE t SET s1 = s1 + 1; <--

ELSE

UPDATE t SET s1 = s1 + 2;

END IF;

END; //

  因為參數parameter1值等於0,UPDATE語句被執行。如果parameter1值為NULL,則下一條UPDATE語句將被執行現在表t中有兩行,他們都包含值5,所以如果我們調用p12,兩行的值會變成6。

  7.

mysql> CALL p12(0)//

Query OK, 2 rows affected (0.28 sec)

MySQL> SELECT * FROM t//

+------+

| s1 |

+------+

| 6 |

| 6 |

+------+

2 rows in set (0.01 sec)

  結果也是我們所期望的那樣。













CASE 指令

  1.

CREATE PROCEDURE p13 (IN parameter1 INT)

BEGIN

DECLARE variable1 INT;

SET variable1 = parameter1 + 1;

CASE variable1

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; //

  如果需要進行更多條件真假的判斷我們可以使用CASE語句。CASE語句使用和IF一樣簡單。

  我們可以參考上面的例子: 2.

mysql> CALL p13(1)//

Query OK, 1 row affected (0.00 sec)

MySQL> SELECT * FROM t//

+------+

| s1 |

+------+

| 6 |

| 6 |

| 19 |

+------+

3 rows in set (0.00 sec)

  執行過程後,傳入值1,如上面例子,值19被插入到表t中。

  Question

  問題

  問題: CALL p13(NULL) //的作用是什麼?

  另一個:這個CALL語句做了那些動作?

  你可以通過執行後觀察SELECT做了什麼,也可以根據代碼判斷,在5秒內做出。

 Answer

  答案

mysql> CALL p13(NULL)//

Query OK, 1 row affected (0.00 sec)

MySQL> SELECT * FROM t//

+------+

| s1 |

+------+

| 6 |

| 6 |

| 19 |

| 19 |

+------+

4 rows in set (0.00 sec)

  答案是當你調用p13時,MySQL插入了另一條包含數值19的記錄。原因是變量variable1的值為NULL,CASE語句的ELSE部分就被執行了。希望這對大家有意義。如果你回答不出來,沒有問題,我們可以繼續向下走。

Loops 循環語句
WHILE ... END WHILE

LOOP ... END LOOP

REPEAT ... END REPEAT

GOTO

  下面我們將會創建一些循環。我們有三種標准的循環方式:

  WHILE循環,LOOP循環以及REPEAT循環。還有一種非標准的循環方式:GO TO(譯者語:最好不要用吧,用了就使流程混亂)。







CREATE PROCEDURE p14 ()

BEGIN

DECLARE v INT;

SET v = 0;

WHILE v < 5 DO

INSERT INTO t VALUES (v);

SET v = v + 1;

END WHILE;

END; //

  這是WHILE循環的方式。我很喜歡這種方式,它跟IF語句相似,因此不需要掌握很多新的語法。這裡的INSERT和SET語句在WHILE和END WHILE之間,當變量v大於5的時候循環將會退出。使用

"SET v = 0;"

  語句使為了防止一個常見的錯誤,如果沒有初始化,默認變量值為NULL,而NULL和任何值操作結果都為NULL。

WHILE ... END WHILE example

MySQL> CALL p14()//

Query OK, 1 row affected (0.00 sec)

  以上就是調用過程p14的結果不用關注系統返回是"one row affected"還是"five rows affected",因為這裡的計數只對最後一個INSERT動作進行計數。

WHILE ... END WHILE example: CALL

MySQL> select * from t; //

+------+

| s1 |

+------+

....

| 0 |

| 1 |

| 2 |

| 3 |

| 4 |

+------+

9 rows in set (0.00 sec)

  調用後可以看到程序向數據庫中插入了5行。

REPEAT ... END REPEAT

CREATE PROCEDURE p15 ()

BEGIN

DECLARE v INT;

SET v = 0;

REPEAT

INSERT INTO t VALUES (v);

SET v = v + 1;

UNTIL v >= 5

END REPEAT;

END; //

  這是一個REPEAT循環的例子,功能和前面WHILE循環一樣。區別在於它在執行後檢查結果,而WHILE則是執行前檢查。(譯者語:可能等同於DO WHILE吧)

REPEAT ... END REPEAT: look at the UNTIL: UNTIL的作用

CREATE PROCEDURE p15 ()

BEGIN

DECLARE v INT;

SET v = 0;

REPEAT

INSERT INTO t VALUES (v);

SET v = v + 1;

UNTIL v >= 5 <--

END REPEAT;

END; //

  注意到UNTIL語句後面沒有分號,在這裡可以不寫分號,當然你加上額外的分號更好。

  REPEAT ... END REPEAT: calling :調用

mysql> CALL p15()//

Query OK, 1 row affected (0.00 sec)

MySQL> SELECT COUNT(*) FROM t//

+----------+

| COUNT(*) |

+----------+

| 14 |

+----------+

1 row in set (0.00 sec)

  我們可以看到調用p15過程後又插入了5行記錄

LOOP ... END LOOP

CREATE PROCEDURE p16 ()

BEGIN

DECLARE v INT;

SET v = 0;

loop_label: LOOP

INSERT INTO t VALUES (v);

SET v = v + 1;

IF v >= 5 THEN

LEAVE loop_label;

END IF;

END LOOP;

END; //

  以上是LOOP循環的例子。

  LOOP循環不需要初始條件,這點和WHILE循環相似,同時它又和REPEAT循環一樣也不需要結束條件。

  LOOP ... END LOOP: with IF and LEAVE 包含IF和LEAVE的LOOP循環

CREATE PROCEDURE p16 ()

BEGIN

DECLARE v INT;

SET v = 0;

loop_label: LOOP

INSERT INTO t VALUES (v);

SET v = v + 1;

IF v >= 5 THEN <--

LEAVE loop_label;

END IF;

END LOOP;

END; //

  在循環內部加入IF語句,在IF語句中包含LEAVE語句。這裡LEAVE語句的意義是離開循環。

  LEAVE的語法是LEAVE加循環語句標號,關於循環語句的標號問題我會在後面進一步講解。

  LOOP ... END LOOP: calling :調用

mysql> CALL p16()//

Query OK, 1 row affected (0.00 sec)

MySQL> SELECT COUNT(*) FROM t//

+----------+

| COUNT(*) |

+----------+

| 19 |

+----------+

1 row in set (0.00 sec)

  調用過程p16後,結果是另5行被插入表t中。

Labels 標號

CREATE PROCEDURE p17 ()

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; //

  最後一個循環例子中我使用了語句標號。現在這裡有一個包含4個語句標號的過程的例子。我們可以在BEGIN、WHILE、REPEAT或者LOOP語句前使用語句標號,語句標號只能在合法的語句前面使用。因此"LEAVE label_3"意味著離開語句標號名定義為label_3的語句或復合語句。






End Labels 標號結束符

CREATE PROCEDURE p18 ()

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 ; //

  你也可以在語句結束時使用語句標號,和在開頭時使用一樣。這些標號結束符並不是十分有用。

  它們是可選的。如果你需要,他們必須和開始定義的標號名字一樣當然為了有良好的編程習慣,方便他人閱讀,最好還是使用標號結束符。

LEAVE and Labels 跳出和標號

CREATE PROCEDURE p19 (parameter1 CHAR)

label_1: BEGIN

label_2: BEGIN

label_3: BEGIN

IF parameter1 IS NOT NULL THEN

IF parameter1 = 'a' THEN

LEAVE label_1;

ELSE BEGIN

IF parameter1 = 'b' THEN

LEAVE label_2;

ELSE

LEAVE label_3;

END IF;

END;

END IF;

END IF;

END;

END;

END;//

LEAVE

  語句使程序跳出復雜的復合語句。

ITERATE

  迭代如果目標是ITERATE(迭代)語句的話,就必須用到LEAVE語句

CREATE PROCEDURE p20 ()

BEGIN

DECLARE v INT;

SET v = 0;

loop_label: LOOP

IF v = 3 THEN

SET v = v + 1;

ITERATE loop_label;

END IF;

INSERT INTO t VALUES (v);

SET v = v + 1;

IF v >= 5 THEN

LEAVE loop_label;

END IF;

END LOOP;

END; //


ITERATE

  (迭代)語句和LEAVE語句一樣也是在循環內部的循環引用,它有點像C語言中的“Continue”,同樣它可以出現在復合語句中,引用復合語句標號,ITERATE(迭代)意思是重新開始復合語句。





  那我們啟動並觀察下面這個循環,這是個需要迭代過程的循環:

ITERATE: Walking through the loop

深入循環

CREATE PROCEDURE p20 ()

BEGIN

DECLARE v INT;

SET v = 0;

loop_label: LOOP <--

IF v = 3 THEN

SET v = v + 1;

ITERATE loop_label;

END IF;

INSERT INTO t VALUES (v);

SET v = v + 1;

IF v >= 5 THEN

LEAVE loop_label;

END IF;

END LOOP;

END; //

  讓這個已經定義了標號的循環運行起來。

ITERATE: Walking through the loop

CREATE PROCEDURE p20 ()

BEGIN

DECLARE v INT;

SET v = 0;

loop_label: LOOP

IF v = 3 THEN <--

SET v = v + 1;

ITERATE loop_label;

END IF;

INSERT INTO t VALUES (v);

SET v = v + 1;

IF v >= 5 THEN

LEAVE loop_label;

END IF;

END LOOP;

END; //

  v的值變成3,然後我們把它增加到4。

ITERATE: walking through the loop

CREATE PROCEDURE p20 ()

BEGIN

DECLARE v INT;

SET v = 0;

loop_label: LOOP

IF v = 3 THEN

SET v = v + 1;

ITERATE loop_label; <--

END IF;

INSERT INTO t VALUES (v);

SET v = v + 1;

IF v >= 5 THEN

LEAVE loop_label;

END IF;

END LOOP;

END; //

  然後開始ITERATE(迭代)過程。

ITERATE: walking through the loop

CREATE PROCEDURE p20 ()

BEGIN

DECLARE v INT;

SET v = 0;

loop_label: LOOP <--

IF v = 3 THEN

SET v = v + 1;

ITERATE loop_label;

END IF;

INSERT INTO t VALUES (v);

SET v = v + 1;

IF v >= 5 THEN

LEAVE loop_label;

END IF;

END LOOP;

END; //

 這裡的ITERATE(迭代)讓循環又回到了循環的頭部。

ITERATE: walking through the loop

CREATE PROCEDURE p20 ()

BEGIN

DECLARE v INT;

SET v = 0;

loop_label: LOOP

IF v = 3 THEN

SET v = v + 1;

ITERATE loop_label;

END IF;

INSERT INTO t VALUES (v);

SET v = v + 1;

IF v >= 5 THEN

LEAVE loop_label; <--

END IF;

END LOOP;

END; //

  當v的值變為5時,程序將執行LEAVE語句

ITERATE: walking through the loop

CREATE PROCEDURE p20 ()

BEGIN

DECLARE v INT;

SET v = 0;

loop_label: LOOP

IF v = 3 THEN

SET v = v + 1;

ITERATE loop_label;

END IF;

INSERT INTO t VALUES (v);

SET v = v + 1;

IF v >= 5 THEN

LEAVE loop_label;

END IF;

END LOOP;

END; // <--

 LEAVE的結果就是跳出循環,使運行指令到達復合語句的最後一步。

GOTO

CREATE PROCEDURE p...

BEGIN

...

LABEL label_name;

...

GOTO label_name;

...

END;

  MySQL的存儲過程中可以使用GOTO語句。雖然這不是標准SQL語句,而且在這裡建立標號的方法也和慣例中的不一樣。由於為了和其他DBMS兼容,這個語句會慢被淘汰,所以我們在MySQL參考手冊中沒有提及。

Grand combination

大組合

CREATE PROCEDURE p21

(IN parameter_1 INT, OUT parameter_2 INT)

LANGUAGE SQL DETERMINISTIC SQL SECURITY INVOKER

BEGIN

DECLARE v INT;

label goto_label; start_label: LOOP

IF v = v THEN LEAVE start_label;

ELSE ITERATE start_label;

END IF;

END LOOP start_label;

REPEAT

WHILE 1 = 0 DO BEGIN END;

END WHILE;

UNTIL v = v END REPEAT;

GOTO goto_label;

END;//

  上面例子中的語句包含了我們之前講的所有語法,包括參數列表,特性參數,BEGIN/END塊復合語句,變量聲明,IF,WHILE,LOOP,REPEAT,LEAVE,ITERATE,GOTO。這是一個荒謬的存儲過程,我不會運行它,因為裡面有無盡的循環。但是裡面的語法卻十分合法。這些是新的流程控制和變量聲明語句。下面我們將要接觸更多新的東西。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved