mysql存儲過程詳解 mysql存儲過程和函數

第20章:存儲程序和函數

目錄mysql

20.1. 存儲程序和受權表sql

20.2. 存儲程序的語法數據庫

20.2.1. CREATE PROCEDURE和CREATE FUNCTION編程

20.2.2. ALTER PROCEDURE和ALTER FUNCTION安全

20.2.3. DROP PROCEDURE和和DROP FUNCTION服務器

20.2.4. SHOW CREATE PROCEDURE和SHOW CREATE FUNCTION框架

20.2.5. SHOW PROCEDURE STATUS和SHOW FUNCTION STATUSless

20.2.6. CALL語句函數

20.2.7. BEGIN ... END複合語句性能

20.2.8. DECLARE語句

20.2.9. 存儲程序中的變量

20.2.10. 條件和處理程序

20.2.11. 光標

20.2.12. 流程控制構造

20.3. 存儲程序、函數、觸發程序和複製:常見問題

20.4. 存儲子程序和觸發程序的二進制日誌功能

MySQL 5.1版支持存儲程序和函數。一個存儲程序是能夠被存儲在服務器中的一套SQL語句。一旦它被存儲了,客戶端不須要再從新發布單獨的語句,而是能夠引用存儲程序來替代。

下面一些狀況下存儲程序尤爲有用:

·         當用不一樣語言編寫多客戶應用程序,或多客戶應用程序在不一樣平臺上運行且須要執行相同的數據庫操做之時。

·         安全極爲重要之時。好比,銀行對全部普通操做使用存儲程序。這提供一個堅固而安全的環境,程序能夠確保每個操做都被妥善記入日誌。在這樣一個設置中,應用程序和用戶不可能直接訪問數據庫表,可是僅能夠執行指定的存儲程序。

  存儲程序能夠提供改良後的性能,由於只有較少的信息須要在服務器和客戶算之間傳送。代價是增長數據庫服務器系統的負荷,由於更多的工做在服務器這邊完成,更少的在客戶端(應用程序)那邊完成上。若是許多客戶端機器(好比網頁服務器)只由一個或少數幾個數據庫服務器提供服務,能夠考慮一下存儲程序。

  存儲程序也容許你在數據庫服務器上有函數庫。這是一個被現代應用程序語言共享的特徵,它容許這樣的內部設計,好比經過使用類。使用這些客戶端應用程序語言特徵對甚至於數據庫使用範圍之外的編程人員都有好處。

  MySQL爲存儲程序遵循SQL:2003語法,這個語法也被用在IBM的DB2數據庫上。

  MySQL對存儲程序的實現還在進度中。全部本章敘述的語法都被支持,在有限制或擴展的地方會恰當地指出來。有關使用存儲程序的限制的更多討論在附錄 I, 特性限制裏提到。

  如20.4節,「存儲子程序和觸發程序的二進制日誌功能」裏所說的,存儲子程序的二進制日誌功能已經完成。

20.1. 存儲程序和受權表

  存儲程序須要在mysql數據庫中有proc表。這個表在MySQL 5.1安裝過程當中建立。若是你從早期的版本升級到MySQL 5.1 ,請肯定更新你的受權表以確保proc表的存在。

在MySQL 5.1中,受權系統以下考慮存儲子程序:

·         建立存儲子程序須要CREATE ROUTINE權限。

·         提醒或移除存儲子程序須要ALTER ROUTINE權限。這個權限自動授予子程序的建立者。

·         執行子程序須要EXECUTE權限。然而,這個權限自動授予子程序的建立者。一樣,子程序默認的SQL SECURITY 特徵是DEFINER,它容許用該子程序訪問數據庫的用戶與執行子程序聯繫到一塊兒。

20.2. 存儲程序的語法

  存儲程序和函數是用CREATE PROCEDURE和CREATE FUNCTION語句建立的子程序。一個子程序要麼是一個程序要麼是一個函數。使用CALL語句來調用程序,程序只能用輸出變量傳回值。就像別其它函數調用同樣,函數能夠被從語句外調用(即經過引用函數名),函數能返回標量值。存儲子程序也能夠調用其它存儲子程序。

  在MySQL 5.1中,一個存儲子程序或函數與特定的數據庫相聯繫。這裏有幾個意思:

·         當一個子程序被調用時,一個隱含的USE db_name 被執行(當子程序終止時中止執行)。存儲子程序內的USE語句時不容許的。

·         你可使用數據庫名限定子程序名。這能夠被用來引用一個不在當前數據庫中的子程序。好比,要引用一個與test數據庫關聯的存儲程序p或函數f,你能夠說CALL test.p()或test.f()。

·         數據庫移除的時候,與它關聯的全部存儲子程序也都被移除。

  MySQL 支持很是有用的擴展,即它容許在存儲程序中使用常規的SELECT語句(那就是說,不使用光標或局部變量)。這個一個查詢的結果包被簡單地直接送到客戶端。多SELECT語句生成多個結果包,因此客戶端必須使用支持多結果包的MySQL客戶端庫。這意味這客戶端必須使用至少MySQL 4.1以來的近期版本上的客戶端庫。

  下面一節描述用來建立,改變,移除和查詢存儲程序和函數的語法。

20.2.1. CREATE PROCEDURE和CREATE FUNCTION

CREATE PROCEDURE sp_name ([proc_parameter[,...]])
    [characteristic ...] routine_body
 
CREATE FUNCTION sp_name ([func_parameter[,...]])
    RETURNS type
    [characteristic ...] routine_body
    
    proc_parameter:
    [ IN | OUT | INOUT ] param_name type
    
    func_parameter:
    param_name type
 
type:
    Any valid MySQL data type
 
characteristic:
    LANGUAGE SQL
  | [NOT] DETERMINISTIC
  | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
  | SQL SECURITY { DEFINER | INVOKER }
  | COMMENT 'string'
 
routine_body:
    Valid SQL procedure statement or statements

  這些語句建立存儲子程序。要在MySQL 5.1中建立子程序,必須具備CREATE ROUTINE權限,而且ALTER ROUTINE和EXECUTE權限被自動授予它的建立者。若是二進制日誌功能被容許,你也可能須要SUPER權限,請參閱20.4節,「存儲子程序和觸發程序的二進制日誌功能」。

  默認地,子程序與當前數據庫關聯。要明確地把子程序與一個給定數據庫關聯起來,能夠在建立子程序的時候指定其名字爲db_name.sp_name

  若是子程序名和內建的SQL函數名同樣,定義子程序時,你須要在這個名字和隨後括號中間插入一個空格,不然發生語法錯誤。當你隨後調用子程序的時候也要插入。爲此,即便有可能出現這種狀況,咱們仍是建議最好避免給你本身的存儲子程序取與存在的SQL函數同樣的名字。

  由括號包圍的參數列必須老是存在。若是沒有參數,也該使用一個空參數列()。每一個參數默認都是一個IN參數。要指定爲其它參數,可在參數名以前使用關鍵詞 OUT或INOUT

  注意: 指定參數爲IN, OUT, 或INOUT 只對PROCEDURE是合法的。(FUNCTION參數老是被認爲是IN參數)

  RETURNS字句只能對FUNCTION作指定,對函數而言這是強制的。它用來指定函數的返回類型,並且函數體必須包含一個RETURN value語句。

  routine_body 包含合法的SQL過程語句。可使用複合語句語法,請參閱20.2.7節,「BEGIN ... END複合語句」。複合語句能夠包含聲明,循環和其它控制結構語句。這些語句的語法在本章後免介紹,舉例,請參閱20.2.8節,「DECLARE語句」和20.2.12節,「流程控制構造」。

  CREATE FUNCTION語句被用在更早的MySQL版本上以支持UDF (自定義函數)。請參閱27.2節,「給MySQL添加新函數」。 UDF繼續被支持,即便如今有了存儲函數。UDF會被認爲一個外部存儲函數。然而,不要讓存儲函數與UDF函數共享名字空間。

  外部存儲程序的框架將在不久的未來引入。這將容許你用SQL以外的語言編寫存儲程序。最可能的是,第一個被支持語言是PHP,由於核心PHP引擎很小,線程安全,且能夠被方便地嵌入。由於框架是公開的,它但願許多其它語言也能被支持。

  若是程序或線程老是對一樣的輸入參數產生一樣的結果,則被認爲它是「肯定的」,不然就是「非肯定」的。若是既沒有給定DETERMINISTIC也沒有給定NOT DETERMINISTIC,默認的就是NOT DETERMINISTIC。

  爲進行復制,使用NOW()函數(或它的同義詞)或RAND()函數會沒必要要地使得一個子程序非肯定。對NOW()而言,二進制日誌包括時間戳並被正確複製。RAND() 只要在一個子程序被內應用一次也會被正確複製。(你能夠把子程序執行時間戳和隨機數種子認爲強制輸入,它們在主從上是一樣的。)

  當前來說,DETERMINISTIC特徵被接受,但尚未被優化程序所使用。然而若是二進制日誌功能被容許了,這個特徵影響到MySQL是否會接受子程序定義。請參閱20.4節,「存儲子程序和觸發程序的二進制日誌功能」。

  一些特徵提供子程序使用數據的內在信息。CONTAINS SQL表示子程序不包含讀或寫數據的語句。NO SQL表示子程序不包含SQL語句。READS SQL DATA表示子程序包含讀數據的語句,但不包含寫數據的語句。MODIFIES SQL DATA表示子程序包含寫數據的語句。若是這些特徵沒有明確給定,默認的是CONTAINS SQL。

  SQL SECURITY特徵能夠用來指定子程序該用建立子程序者的許可來執行,仍是使用調用者的許可來執行。默認值是DEFINER。在SQL:2003中者是一個新特性。建立者或調用者必須由訪問子程序關聯的數據庫的許可。在MySQL 5.1中,必須有EXECUTE權限才能執行子程序。必須擁有這個權限的用戶要麼是定義者,要麼是調用者,這取決於SQL SECURITY特徵是如何設置的。

  MySQL存儲sql_mode系統變量設置,這個設置在子程序被建立的時候起做用,MySQL老是強制使用這個設置來執行子程序。

  COMMENT子句是一個MySQL的擴展,它能夠被用來描述存儲程序。這個信息被SHOW CREATE PROCEDURE和 SHOW CREATE FUNCTION語句來顯示。

  MySQL容許子程序包含DDL語句,如CREATE和DROP。MySQL也容許存儲程序(但不是存儲函數)包含SQL 交互語句,如COMMIT。存儲函數不能夠包含那些作明確的和絕對的提交或者作回滾的語。SQL標準不要求對這些語句的支持,SQL標準聲明每一個DBMS提供商能夠決定是否容許支持這些語句。

存儲子程序不能使用LOAD DATA INFILE。

  返回結果包的語句不能被用在存儲函數種。這包括不使用INTO給變量讀取列值的SELECT語句,SHOW 語句,及其它諸如EXPLAIN這樣的語句。對於可在函數定義時間被決定要返回一個結果包的語句,發生一個容許從函數錯誤返回結果包的Not(ER_SP_NO_RETSET_IN_FUNC)。對於只可在運行時決定要返回一個結果包的語句, 發生一個不能在給定上下文錯誤返回結果包的PROCEDURE %s (ER_SP_BADSELECT)。

  下面是一個使用OUT參數的簡單的存儲程序的例子。例子爲,在程序被定義的時候,用mysql客戶端delimiter命令來把語句定界符從 ;變爲//。這就容許用在程序體中的;定界符被傳遞到服務器而不是被mysql本身來解釋。

mysql> delimiter //
mysql> CREATE PROCEDURE simpleproc (OUT param1 INT)
    -> BEGIN
    ->   SELECT COUNT(*) INTO param1 FROM t;
    -> END
    -> //
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter ;
mysql> CALL simpleproc(@a);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @a;
+------+
| @a   |
+------+
| 3    |
+------+
1 row in set (0.00 sec)

當使用delimiter命令時,你應該避免使用反斜槓(‘\’)字符,由於那是MySQL的轉義字符。

下列是一個例子,一個採用參數的函數使用一個SQL函數執行一個操做,並返回結果:

mysql> delimiter //
 
mysql> CREATE FUNCTION hello (s CHAR(20)) RETURNS CHAR(50)
    -> RETURN CONCAT('Hello, ',s,'!');
    -> //
Query OK, 0 rows affected (0.00 sec)
 
mysql> delimiter ;
 
mysql> SELECT hello('world');
+----------------+
| hello('world') |
+----------------+
| Hello, world!  |
+----------------+
1 row in set (0.00 sec)

  若是在存儲函數中的RETURN語句返回一個類型不一樣於在函數的RETURNS子句中指定類型的值,返回值被強制爲恰當的類型。好比,若是一個函數返回一個ENUM或SET值,可是RETURN語句返回一個整數,對於SET成員集的相應的ENUM成員,從函數返回的值是字符串。

20.2.2. ALTER PROCEDURE和ALTER FUNCTION

ALTER {PROCEDURE | FUNCTION} sp_name [characteristic ...]
characteristic:
    { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
  | SQL SECURITY { DEFINER | INVOKER }
  | COMMENT 'string'

  這個語句能夠被用來改變一個存儲程序或函數的特徵。在MySQL 5.1中,你必須用ALTER ROUTINE權限纔可用此子程序。這個權限被自動授予子程序的建立者。如20.4節,「存儲子程序和觸發程序的二進制日誌功能」中所述, 若是二進制日誌功能被容許了,你可能也須要SUPER權限。

  在ALTER PROCEDURE和ALTER FUNCTION語句中,能夠指定超過一個的改變。

20.2.3. DROP PROCEDURE和DROP FUNCTION

DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name

  這個語句被用來移除一個存儲程序或函數。即,從服務器移除一個制定的子程序。在MySQL 5.1中,你必須有ALTER ROUTINE權限纔可用此子程序。這個權限被自動授予子程序的建立者。

  IF EXISTS 子句是一個MySQL的擴展。若是程序或函數不存儲,它防止發生錯誤。產生一個能夠用SHOW WARNINGS查看的警告。

20.2.4. SHOW CREATE PROCEDURE和SHOW CREATE FUNCTION

SHOW CREATE {PROCEDURE | FUNCTION} sp_name

  這個語句是一個MySQL的擴展。相似於SHOW CREATE TABLE,它返回一個可用來從新建立已命名子程序的確切字符串。

mysql> SHOW CREATE FUNCTION test.hello\G
*************************** 1. row ***************************
       Function: hello
       sql_mode:
Create Function: CREATE FUNCTION `test`.`hello`(s CHAR(20)) RETURNS CHAR(50)
RETURN CONCAT('Hello, ',s,'!')

20.2.5. SHOW PROCEDURE STATUS和SHOW FUNCTION STATUS

SHOW {PROCEDURE | FUNCTION} STATUS [LIKE 'pattern']

  這個語句是一個MySQL的擴展。它返回子程序的特徵,如數據庫,名字,類型,建立者及建立和修改日期。若是沒有指定樣式,根據你使用的語句,全部存儲程序和全部存儲函數的信息都被列出。

mysql> SHOW FUNCTION STATUS LIKE 'hello'\G
*************************** 1. row ***************************
           Db: test
         Name: hello
         Type: FUNCTION
      Definer: testuser@localhost
     Modified: 2004-08-03 15:29:37
      Created: 2004-08-03 15:29:37
Security_type: DEFINER
      Comment:

  你能夠從INFORMATION_SCHEMA中的ROUTINES表得到有關存儲子程序的信息。請參閱23.1.14節,「INFORMATION_SCHEMA ROUTINES 表」。

20.2.6. CALL語句

CALL sp_name([parameter[,...]])

  CALL語句調用一個先前用CREATE PROCEDURE建立的程序。

  CALL語句能夠用聲明爲OUT或的INOUT參數的參數給它的調用者傳回值。它也「返回」受影響的行數,客戶端程序能夠在SQL級別經過調用ROW_COUNT()函數得到這個數,從C中是調用the mysql_affected_rows() C API函數來得到。

20.2.7. BEGIN ... END複合語句

[begin_label:] BEGIN
    [statement_list]
END [end_label]

  存儲子程序可使用BEGIN ... END複合語句來包含多個語句。statement_list 表明一個或多個語句的列表。statement_list以內每一個語句都必須用分號(;)來結尾。

  複合語句能夠被標記。除非begin_label存在,不然end_label不能被給出,而且若是兩者都存在,他們必須是一樣的。

  請注意,可選的[NOT] ATOMIC子句如今還不被支持。這意味着在指令塊的開始沒有交互的存儲點被設置,而且在上下文中用到的BEGIN子句對當前交互動做沒有影響。

  使用多重語句須要客戶端能發送包含語句定界符;的查詢字符串。這個符號在命令行客戶端被用delimiter命令來處理。改變查詢結尾定界符;(好比改變爲//)使得; 可被用在子程序體中。

20.2.8. DECLARE語句

  DECLARE語句被用來把不一樣項目局域到一個子程序:局部變量(請參閱20.2.9節,「存儲程序中的變量」),條件和處理程序(請參閱20.2.10節,「條件和處理程序」) 及光標(請參閱20.2.11節,「光標」)。SIGNAL和RESIGNAL語句當前還不被支持。

  DECLARE僅被用在BEGIN ... END複合語句裏,而且必須在複合語句的開頭,在任何其它語句以前。

  光標必須在聲明處理程序以前被聲明,而且變量和條件必須在聲明光標或處理程序以前被聲明。

20.2.9. 存儲程序中的變量

20.2.9.1. DECLARE局部變量

20.2.9.2. 變量SET語句

20.2.9.3. SELECT ... INTO語句

你能夠在子程序中聲明並使用變量。

20.2.9.1. DECLARE局部變量

DECLARE var_name[,...] type [DEFAULT value]

  這個語句被用來聲明局部變量。要給變量提供一個默認值,請包含一個DEFAULT子句。值能夠被指定爲一個表達式,不須要爲一個常數。若是沒有DEFAULT子句,初始值爲NULL。

  局部變量的做用範圍在它被聲明的BEGIN ... END塊內。它能夠被用在嵌套的塊中,除了那些用相同名字聲明變量的塊。

20.2.9.2. 變量SET語句

SET var_name = expr [, var_name = expr] ...

  在存儲程序中的SET語句是通常SET語句的擴展版本。被參考變量多是子程序內聲明的變量,或者是全局服務器變量。

  在存儲程序中的SET語句做爲預先存在的SET語法的一部分來實現。這容許SET a=x, b=y, ...這樣的擴展語法。其中不一樣的變量類型(局域聲明變量及全局和集體變量)能夠被混合起來。這也容許把局部變量和一些只對系統變量有意義的選項合併起來。在那種狀況下,此選項被識別,可是被忽略了。

20.2.9.3. SELECT ... INTO語句

SELECT col_name[,...] INTO var_name[,...] table_expr

這個SELECT語法把選定的列直接存儲到變量。所以,只有單一的行能夠被取回。

SELECT id,data INTO x,y FROM test.t1 LIMIT 1;

  注意,用戶變量名在MySQL 5.1中是對大小寫不敏感的。請參閱9.3節,「用戶變量」。

  重要: SQL變量名不能和列名同樣。若是SELECT ... INTO這樣的SQL語句包含一個對列的參考,幷包含一個與列相同名字的局部變量,MySQL當前把參考解釋爲一個變量的名字。例如,在下面的語句中,xname 被解釋爲到xname variable 的參考而不是到xname column的:

CREATE PROCEDURE sp1 (x VARCHAR(5))
  BEGIN
    DECLARE xname VARCHAR(5) DEFAULT 'bob';
    DECLARE newname VARCHAR(5);
    DECLARE xid INT;
    
    SELECT xname,id INTO newname,xid 
      FROM table1 WHERE xname = xname;
    SELECT newname;
  END;

  當這個程序被調用的時候,不管table.xname列的值是什麼,變量newname將返回值‘bob’。

  請參閱I.1節,「存儲子程序和觸發程序的限制」。

20.2.10. 條件和處理程序

20.2.10.1. DECLARE條件

20.2.10.2. DECLARE處理程序

特定條件須要特定處理。這些條件能夠聯繫到錯誤,以及子程序中的通常流程控制。

20.2.10.1. DECLARE條件

DECLARE condition_name CONDITION FOR condition_value
 condition_value:
    SQLSTATE [VALUE] sqlstate_value
  | mysql_error_code

  這個語句指定須要特殊處理的條件。它將一個名字和指定的錯誤條件關聯起來。這個名字能夠隨後被用在DECLARE HANDLER語句中。請參閱20.2.10.2節,「DECLARE處理程序」。

  除了SQLSTATE值,也支持MySQL錯誤代碼。

20.2.10.2. DECLARE處理程序

DECLARE handler_type HANDLER FOR condition_value[,...] sp_statement
handler_type:
    CONTINUE
  | EXIT
  | UNDO
condition_value:
    SQLSTATE [VALUE] sqlstate_value
  | condition_name
  | SQLWARNING
  | NOT FOUND
  | SQLEXCEPTION
  | mysql_error_code

  這個語句指定每一個能夠處理一個或多個條件的處理程序。若是產生一個或多個條件,指定的語句被執行。

  對一個CONTINUE處理程序,當前子程序的執行在執行處理程序語句以後繼續。對於EXIT處理程序,當前BEGIN...END複合語句的執行被終止。UNDO 處理程序類型語句還不被支持。

·         SQLWARNING是對全部以01開頭的SQLSTATE代碼的速記。

·         NOT FOUND是對全部以02開頭的SQLSTATE代碼的速記。

·         SQLEXCEPTION是對全部沒有被SQLWARNING或NOT FOUND捕獲的SQLSTATE代碼的速記。

  除了SQLSTATE值,MySQL錯誤代碼也不被支持。

例如:

mysql> CREATE TABLE test.t (s1 int,primary key (s1));
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter //
mysql> CREATE PROCEDURE handlerdemo ()
    -> BEGIN
    ->   DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
    ->   SET @x = 1;
    ->   INSERT INTO test.t VALUES (1);
    ->   SET @x = 2;
    ->   INSERT INTO test.t VALUES (1);
    ->   SET @x = 3;
    -> END;
    -> //
Query OK, 0 rows affected (0.00 sec)
mysql> CALL handlerdemo()//
Query OK, 0 rows affected (0.00 sec) 
mysql> SELECT @x//
    +------+
    | @x   |
    +------+
    | 3    |
    +------+
    1 row in set (0.00 sec)

注意到,@x是3,這代表MySQL被執行到程序的末尾。若是DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; 這一行不在,第二個INSERT因PRIMARY KEY強制而失敗以後,MySQL可能已經採起默認(EXIT)路徑,而且SELECT @x可能已經返回2。

20.2.11. 光標

20.2.11.1.聲明光標

20.2.11.2. 光標OPEN語句

20.2.11.3. 光標FETCH語句

20.2.11.4. 光標CLOSE語句

簡單光標在存儲程序和函數內被支持。語法如同在嵌入的SQL中。光標當前是不敏感的,只讀的及不滾動的。不敏感意爲服務器能夠活不能夠複製它的結果表。

光標必須在聲明處理程序以前被聲明,而且變量和條件必須在聲明光標或處理程序以前被聲明。

例如:

CREATE PROCEDURE curdemo()
BEGIN
  DECLARE done INT DEFAULT 0;
  DECLARE a CHAR(16);
  DECLARE b,c INT;
  DECLARE cur1 CURSOR FOR SELECT id,data FROM test.t1;
  DECLARE cur2 CURSOR FOR SELECT i FROM test.t2;
  DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
  OPEN cur1;
  OPEN cur2; 
  REPEAT
    FETCH cur1 INTO a, b;
    FETCH cur2 INTO c;
    IF NOT done THEN
       IF b < c THEN
          INSERT INTO test.t3 VALUES (a,b);
       ELSE
          INSERT INTO test.t3 VALUES (a,c);
       END IF;
    END IF;
  UNTIL done END REPEAT;
  CLOSE cur1;
  CLOSE cur2;
END

20.2.11.1.聲明光標

DECLARE cursor_name CURSOR FOR select_statement

這個語句聲明一個光標。也能夠在子程序中定義多個光標,可是一個塊中的每個光標必須有惟一的名字。

SELECT語句不能有INTO子句。

20.2.11.2. 光標OPEN語句

OPEN cursor_name

這個語句打開先前聲明的光標。

20.2.11.3. 光標FETCH語句

FETCH cursor_name INTO var_name [, var_name] ...

這個語句用指定的打開光標讀取下一行(若是有下一行的話),而且前進光標指針。

20.2.11.4. 光標CLOSE語句

CLOSE cursor_name

這個語句關閉先前打開的光標。

若是未被明確地關閉,光標在它被聲明的複合語句的末尾被關閉。

20.2.12. 流程控制構造

20.2.12.1. IF語句

20.2.12.2. CASE語句

20.2.12.3. LOOP語句

20.2.12.4. LEAVE語句

20.2.12.5. ITERATE語句

20.2.12.6. REPEAT語句

20.2.12.7. WHILE語句

IF, CASE, LOOP, WHILE, ITERATE, 及 LEAVE 構造被徹底實現。

這些構造可能每一個包含要麼一個單獨語句,要麼是使用BEGIN ... END複合語句的一塊語句。構造能夠被嵌套。

目前還不支持FOR循環。

20.2.12.1. IF語句

IF search_condition THEN statement_list
    [ELSEIF search_condition THEN statement_list] ...
    [ELSE statement_list]
END IF

IF實現了一個基本的條件構造。若是search_condition求值爲真,相應的SQL語句列表被執行。若是沒有search_condition匹配,在ELSE子句裏的語句列表被執行。statement_list能夠包括一個或多個語句。

請注意,也有一個IF() 函數,它不一樣於這裏描述的IF語句。請參閱12.2節,「控制流程函數」。

20.2.12.2. CASE語句

CASE case_value
    WHEN when_value THEN statement_list
    [WHEN when_value THEN statement_list] ...
    [ELSE statement_list]
END CASE
Or:

CASE
    WHEN search_condition THEN statement_list
    [WHEN search_condition THEN statement_list] ...
    [ELSE statement_list]
END CASE

存儲程序的CASE語句實現一個複雜的條件構造。若是search_condition 求值爲真,相應的SQL被執行。若是沒有搜索條件匹配,在ELSE子句裏的語句被執行。

注意:這裏介紹的用在存儲程序裏的CASE語句與12.2節,「控制流程函數」裏描述的SQL CASE表達式的CASE語句有輕微不一樣。這裏的CASE語句不能有ELSE NULL子句,而且用END CASE替代END來終止。

20.2.12.3. LOOP語句

[begin_label:] LOOP
    statement_list
END LOOP [end_label]

LOOP容許某特定語句或語句羣的重複執行,實現一個簡單的循環構造。在循環內的語句一直重複直循環被退出,退出一般伴隨着一個LEAVE 語句。

LOOP語句能夠被標註。除非begin_label存在,不然end_label不能被給出,而且若是二者都出現,它們必須是一樣的。

20.2.12.4. LEAVE語句

LEAVE label

這個語句被用來退出任何被標註的流程控制構造。它和BEGIN ... END或循環一塊兒被使用。

20.2.12.5. ITERATE語句

ITERATE label

ITERATE只能夠出如今LOOP, REPEAT, 和WHILE語句內。ITERATE意思爲:「再次循環。」

例如:

CREATE PROCEDURE doiterate(p1 INT)
BEGIN
  label1: LOOP
    SET p1 = p1 + 1;
    IF p1 < 10 THEN ITERATE label1; END IF;
    LEAVE label1;
  END LOOP label1;
  SET @x = p1;
END

20.2.12.6. REPEAT語句

[begin_label:] REPEAT
    statement_list
UNTIL search_condition
END REPEAT [end_label]

REPEAT語句內的語句或語句羣被重複,直至search_condition 爲真。

REPEAT 語句能夠被標註。 除非begin_label也存在,end_label才能被用,若是二者都存在,它們必須是同樣的。

例如:

mysql> delimiter //
 
mysql> CREATE PROCEDURE dorepeat(p1 INT)
    -> BEGIN
    ->   SET @x = 0;
    ->   REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT;
    -> END
    -> //
Query OK, 0 rows affected (0.00 sec)
mysql> CALL dorepeat(1000)//
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @x//
+------+
| @x   |
+------+
| 1001 |
+------+
1 row in set (0.00 sec)

20.2.12.7. WHILE語句

[begin_label:] WHILE search_condition DO
    statement_list
END WHILE [end_label]

WHILE語句內的語句或語句羣被重複,直至search_condition 爲真。

WHILE語句能夠被標註。 除非begin_label也存在,end_label才能被用,若是二者都存在,它們必須是同樣的。

例如:

CREATE PROCEDURE dowhile()
BEGIN
  DECLARE v1 INT DEFAULT 5;
 
  WHILE v1 > 0 DO
    ...
    SET v1 = v1 - 1;
  END WHILE;
END

20.3. 存儲程序、函數、觸發程序及複製:常見問題

  • MySQL 5.1存儲程序和函數對複製起做用嗎? 

    是的,在存儲程序和函數中被執行標準行爲被從主MySQL服務器複製到從服務器。有少數限制,它們在20.4節,「存儲子程序和 觸發程序二進制日誌功能」中詳述。

  • 在主服務器上建立的存儲程序和函數能夠被複制到從服務器上麼?

    是的,經過通常DDL語句執行的存儲程序和函數,其在主服務器上的建立被複制到從服務器,因此目標將存在兩個服務器上。對存儲程序和函數的ALTER 和DROP語句也被複制。

  • 行爲如何在已複製的存儲程序和函數裏發生?

    MySQL紀錄每一個發生在存儲程序和函數裏的DML事件,並複製這些單獨的行爲到從服務器。執行存儲程序和函數的切實調用不被複制。

  • 對一塊兒使用存儲程序,函數和複製有什麼特別的安全要求麼?

    是的,由於一個從服務器有權限來執行任何讀自主服務器的二進制日誌的語句,指定的安全約束因與複製一塊兒使用的存儲程序和函數而存在。若是複製或二進制日誌大致上是激活的(爲point-in-time恢復的目的),那麼MySQL DBA 有兩個安全選項可選:

    • 任何想建立存儲程序的用戶必須被賦予SUPER權限。
    • 做爲選擇,一個DBA能夠設置log_bin_trust_routine_creators系統變量爲1,它將會容許有標準CREATE ROUTINE權限的人來建立一個存儲程序和函數。
     
  • 對複製存儲程序和函數的行爲有什麼限制?

    嵌入到存儲程序中的不肯定(隨機)或時基行不能適當地複製。隨機產生的結果,僅因其本性,是你可預測的和不能被確實克隆的。所以,複製到從服務器的隨機行爲將不會鏡像那些產生在主服務器上的。注意, 聲明存儲程序或函數爲DETERMINISTIC或者在log_bin_trust_routine_creators中設置系統變量爲0 將會容許隨即值操做被調用。

    此外,時基行爲不能在從服務器上從新產生,由於在存儲程序中經過對複製使用的二進制日誌來計時這樣的時基行爲是不可從新產生的,由於該二進制日誌僅紀錄DML事件且不包括計時約束。

    最後,在大型DML行爲(如大批插入)中非交互表發生錯誤,該非交互表可能經歷複製,在複製版的非交互表中主服務器能夠被部分地從DML行爲更新。可是由於發生的那個錯誤,對從服務器沒有更新。 對函數的DML行爲,工做區將被用IGNORE關鍵詞來執行,以便於在主服務器上致使錯誤的更新被忽略,而且不會致使錯誤的更新被複制到從服務器。

     

  • 上述的限制會影響MySQL做 point-in-time恢復的能力嗎?

    影響複製的同一限制會影響point-in-time恢復。

  •  MySQL要作什麼來改正前述的限制呢?

    未來發行的MySQL預期有一個功能去選擇複製該如何被處理:

    •  基於語句的複製(當前實現)。
    • 行級別複製(它將解決全部早先描述的限制)。
  • 觸發程序對複製起做用麼?

    MySQL 5.1中的觸發程序和複製象在大多數其它數據庫引擎中同樣工做,在那些引擎中,經過觸發程序在主服務器上執行的行爲不被複制到從服務器。取而代之的是,位於主MySQL服務器的表中的 觸發程序須要在那些存在於任何MySQL從服務器上的表內被建立,以便於觸發程序能夠也能夠在從服務器上被激活。

     

  •  一個行爲如何經過從主服務器上覆制到從服務器上的觸發程序來執行呢?

    首先,主服務器上的觸發程序必須在從服務器上重建。一旦重建了,複製流程就象其它參與到複製中的標準DML語句同樣工做。例如:考慮一個已經插入觸發程序AFTER的EMP表,它位於主MySQL服務器上。一樣的EMP表和AFTER插入 觸發程序也存在於從服務器上。複製流程多是:

1.    對EMP作一個INSERT語句。

2.   EMP上的AFTER觸發程序激活。

3.    INSERT語句被寫進二進制日誌。

4.    從服務器上的複製拾起INSERT語句給EMP表,並在從服務器上執行它。

5.    位於從服務器EMP上的AFTER觸發程序激活。

20.4. 存儲子程序和觸發程序的二進制日誌功能

  這一節介紹MySQL 5.1如何考慮二進制日誌功能來處理存儲子程序(程序和函數) 。這一節也適用於觸發程序。

  二進制日誌包含修改數據庫內容的SQL語句的信息。這個信息以描述修改的事件的形式保存起來。

  二進制日誌有兩個重要目的:

·         複製的基礎是主服務器發送包含在二進制日誌裏的事件到從服務器,從服務器執行這些事件來形成與對主服務器形成的一樣的數據改變,請參閱6.2節,「複製概述」。

·         特定的數據恢復操做許要使用二進制日誌。備份的文件被恢復以後,備份後紀錄的二進制日誌裏的事件被從新執行。這些事件把數據庫帶從備份點的日子帶到當前。請參閱5.9.2.2節,「使用備份恢復」。

  MySQL中,以存儲子程序的二進制日誌功能引起了不少問題,這些在下面討論中列出,做爲參考信息。

  除了要另外注意的以外,這些談論假設你已經經過用--log-bin選項啓動服務器容許了二進制日誌功能。(若是二進制日誌功能不被容許,複製將不可能,爲數據恢復的二進制日誌也不存在。)請參閱5.11.3節,「二進制日誌」。

  對存儲子程序語句的二進制日誌功能的特徵在下面列表中描述。一些條目指出你應該注意到的問題。可是在一些狀況下,有你能夠更改的婦五七設置或你能夠用來處理它們的工做區。

·         CREATE PROCEDURE, CREATE FUNCTION, ALTER PROCEDURE,和ALTER FUNCTION 語句被寫進二進制日誌,CALL, DROP PROCEDURE, 和DROP FUNCTION 也同樣。

  儘管如此,對複製有一個安全暗示:要建立一個子程序,用戶必須有CREATE ROUTINE權限,但有這個權限的用戶不能寫一個子程序在從服務器上執行任何操做。由於在從服務器上的SQL線程用徹底權限來運行。例如,若是主服務器和從服務器分別有服務器ID值1和2,在主服務器上的用戶可能建立並調用以下一個程序:

mysql> delimiter //
mysql> CREATE PROCEDURE mysp ()
    -> BEGIN
    ->   IF @@server_id=2 THEN DROP DATABASE accounting; END IF;
    -> END;
    -> //
mysql> delimiter ;
mysql> CALL mysp();

  CREATE PROCEDURE和CALL語句將被寫進二進制日誌,因此從服務器將執行它們。由於從SQL線程有徹底權限,它將移除accounting數據庫。

  要使容許二進制日誌功能的服務器避免這個危險,MySQL 5.1已經要求存儲程序和函數的建立者除了一般須要的CREATE ROUTINE的權限外,還必須有SUPER 權限。相似地,要使用ALTER PROCEDURE或ALTER FUNCTION,除了ALTER ROUTINE權限外你必須有SUPER權限。沒有SUPER權限,將會發生一個錯誤:

ERROR 1419 (HY000): You do not have the SUPER privilege and
binary logging is enabled (you *might* want to use the less safe
log_bin_trust_routine_creators variable)

  你可能不想強制要求子程序建立者必須有SUPER權限。例如,你係統上全部有CREATE ROUTINE權限的用戶多是有經驗的應用程序開發者。要禁止掉對SUPER權限的要求,設置log_bin_trust_routine_creators 全局系統變量爲1。默認地,這個變量值爲0,但你能夠象這樣改變這樣:

mysql> SET GLOBAL log_bin_trust_routine_creators = 1;

  你也能夠在啓動服務器之時用--log-bin-trust-routine-creators選項來設置容許這個變量。

  若是二進制日誌功能不被容許,log_bin_trust_routine_creators 沒有被用上,子程序建立須要SUPER權限。

·         一個執行更新的非肯定子程序是不可重複的,它能有兩個不如意的影響:

o        它會使得從服務器不一樣於主服務器。

-        恢復的數據與原始數據不一樣。

  要解決這些問題,MySQL強制作下面要求:在主服務器上,除非子程序被聲明爲肯定性的或者不更改數據,不然建立或者替換子程序將被拒絕。這意味着當你建立一個子程序的時候,你必需要麼聲明它是肯定性的,要麼它不改變數據。兩套子程序特徵在這裏適用:

-        DETERMINISTIC和NOT DETERMINISTIC指出一個子程序是否對給定的輸入老是產生一樣的結果。若是沒有給定任一特徵,默認是NOT DETERMINISTIC,因此你必須明確指定DETERMINISTIC來聲明一個子程序是肯定性的。

使用NOW() 函數(或它的同義)或者RAND() 函數不是必要地使也一個子程序非肯定性。對NOW()而言,二進制日誌包括時間戳並正確複製。RAND()只要在一個子程序內被調用一次也能夠正確複製。(你能夠認爲子程序執行時間戳和隨機數種子做爲毫無疑問地輸入,它們在主服務器和從服務器上是同樣的。)

-        CONTAINS SQL, NO SQL, READS SQL DATA, 和 MODIFIES SQL數據提供子程序是讀仍是寫數據的信息。不管NO SQL 仍是READS SQL DATA i都指出,子程序沒有改變數據,但你必須明白地指明這些中的一個,由於若是任何這些特徵沒有被給出,默認的特徵是CONTAINS SQL。

  默認地,要一個CREATE PROCEDURE 或 CREATE FUNCTION 語句被接受,DETERMINISTIC 或 NO SQL與READS SQL DATA 中的一個必須明白地指定,不然會產生以下錯誤:

ERROR 1418 (HY000): This routine has none of DETERMINISTIC, NO SQL,
or READS SQL DATA in its declaration and binary logging is enabled
(you *might* want to use the less safe log_bin_trust_routine_creators
variable)

  若是設置log_bin_trust_routine_creators 爲1, 移除對子程序必須是肯定的或不修改數據的要求。

  注意,子程序本性的評估是基於建立者的「誠實度」 :MySQL不檢查聲明爲肯定性的子程序是否不含產生非肯定性結果的語句。

·         若是子程序返回無錯,CALL語句被寫進二進制日誌,不然就不寫。當一個子程序修改數據失敗了,你會獲得這樣的警告:·               

 ERROR 1417 (HY000): A routine failed and has neither NO SQL nor
·                READS SQL DATA in its declaration and binary logging is enabled; if
·                non-transactional tables were updated, the binary log will miss their
·                changes

  這個記日誌行爲潛在地致使問題.若是一個子程序部分地修改一個非交互表(好比一個MyISAM表able)而且返回一個錯誤,二進制日誌將反映這些變化。要防止這種狀況,你應該在子程序中使用交互表而且在交互動做內修改表。

  在一個子程序內,若是你在INSERT, DELETE, 或者UPDATE裏使用IGNORE關鍵詞來忽略錯誤,可能發生一個部分更新,但沒有錯誤產生。這樣的語句被記錄日誌,且正常複製。

·         若是一個存儲函數在一個如SELECT這樣不修改數據的語句內被調用,即便函數自己更改數據,函數的執行也將不被寫進二進制日誌裏。這個記錄日誌的行爲潛在地致使問題。假設函數myfunc()以下定義:        

  CREATE FUNCTION myfunc () RETURNS INT
·                BEGIN
·                  INSERT INTO t (i) VALUES(1);
·                  RETURN 0;
·                END;

  按照上面定義,下面的語句修改表t,由於myfunc()修改表t, 可是語句不被寫進二進制日誌,由於它是一個SELECT語句:

SELECT myfunc();

  對這個問題的工做區將調用在作更新的語句裏作更新的函數。注意,雖然DO語句有時爲了其估算表達式的副效應而被執行,DO在這裏不是一個工做區,由於它不被寫進二進制日誌。

·         在一個子程序內執行的語句不被寫進二進制日誌。假如你發佈下列語句:

·                CREATE PROCEDURE mysp INSERT INTO t VALUES(1);
·                CALL mysp;

對於這個例子來講,CREATE PROCEDURE 和CALL語句出如今二進制日誌裏,但INSERT語句並未出現。

·         在從服務器上,當決定複製哪一個來自主服務器的事件時,下列限制被應用:--replicate-*-table規則不適用於CALL語句或子程序內的語句:在這些狀況下,老是返回「複製!」

  觸發程序相似於存儲函數,因此前述的評論也適用於觸發程序,除了下列狀況: CREATE TRIGGER沒有可選的DETERMINISTIC特徵,因此觸發程序被假定爲老是肯定性的。然而,這個假設在一些狀況下是非法的。好比,UUID()函數是非肯定性的(不能複製)。你應該當心在觸發程序中使用這個函數。

  觸發程序目前不能更新表,可是在未來會支持。由於這個緣由,若是你沒有SUPER權限且log_bin_trust_routine_creators 被設爲0,獲得的錯誤信息相似於存儲子程序與CREATE TRIGGER產生的錯誤信息。  

  在本節中敘述的問題來自發生在SQL語句級別的二進制日誌記錄的事實。將來發行的MySQL指望能實現行級的二進制日誌記錄,記錄發生在更細緻的級別而且指出哪一個改變做爲執行SQL的結果對單個記錄而作。

相關文章
相關標籤/搜索