?
This document uses PHP Chinese website manual Release
20.2. 存儲(chǔ)程序的語(yǔ)法
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 STATUS
20.2.6. CALL語(yǔ)句
20.2.7. BEGIN ... END復(fù)合語(yǔ)句
20.2.8. DECLARE語(yǔ)句
20.2.9. 存儲(chǔ)程序中的變量
20.2.10. 條件和處理程序
20.2.11. 光標(biāo)
20.2.12. 流程控制構(gòu)造
20.3. 存儲(chǔ)程序、函數(shù)、觸發(fā)程序和復(fù)制:常見問(wèn)題
20.4. 存儲(chǔ)子程序和觸發(fā)程序的二進(jìn)制日志功能
MySQL 5.1版支持存儲(chǔ)程序和函數(shù)。一個(gè)存儲(chǔ)程序是可以被存儲(chǔ)在服務(wù)器中的一套SQL語(yǔ)句。一旦它被存儲(chǔ)了,客戶端不需要再重新發(fā)布單獨(dú)的語(yǔ)句,而是可以引用存儲(chǔ)程序來(lái)替代。
下面一些情況下存儲(chǔ)程序尤其有用:
·???????? 當(dāng)用不同語(yǔ)言編寫多客戶應(yīng)用程序,或多客戶應(yīng)用程序在不同平臺(tái)上運(yùn)行且需要執(zhí)行相同的數(shù)據(jù)庫(kù)操作之時(shí)。
·???????? 安全極為重要之時(shí)。比如,銀行對(duì)所有普通操作使用存儲(chǔ)程序。這提供一個(gè)堅(jiān)固而安全的環(huán)境,程序可以確保每一個(gè)操作都被妥善記入日志。在這樣一個(gè)設(shè)置中,應(yīng)用程序和用戶不可能直接訪問(wèn)數(shù)據(jù)庫(kù)表,但是僅可以執(zhí)行指定的存儲(chǔ)程序。
存儲(chǔ)程序可以提供改良后的性能,因?yàn)橹挥休^少的信息需要在服務(wù)器和客戶算之間傳送。代價(jià)是增加數(shù)據(jù)庫(kù)服務(wù)器系統(tǒng)的負(fù)荷,因?yàn)楦嗟墓ぷ髟诜?wù)器這邊完成,更少的在客戶端(應(yīng)用程序)那邊完成上。如果許多客戶端機(jī)器(比如網(wǎng)頁(yè)服務(wù)器)只由一個(gè)或少數(shù)幾個(gè)數(shù)據(jù)庫(kù)服務(wù)器提供服務(wù),可以考慮一下存儲(chǔ)程序。
存儲(chǔ)程序也允許你在數(shù)據(jù)庫(kù)服務(wù)器上有函數(shù)庫(kù)。這是一個(gè)被現(xiàn)代應(yīng)用程序語(yǔ)言共享的特征,它允許這樣的內(nèi)部設(shè)計(jì),比如通過(guò)使用類。使用這些客戶端應(yīng)用程序語(yǔ)言特征對(duì)甚至于數(shù)據(jù)庫(kù)使用范圍以外的編程人員都有好處。
MySQL為存儲(chǔ)程序遵循SQL:2003語(yǔ)法,這個(gè)語(yǔ)法也被用在IBM的DB2數(shù)據(jù)庫(kù)上。
MySQL對(duì)存儲(chǔ)程序的實(shí)現(xiàn)還在進(jìn)度中。所有本章敘述的語(yǔ)法都被支持,在有限制或擴(kuò)展的地方會(huì)恰當(dāng)?shù)刂赋鰜?lái)。有關(guān)使用 存儲(chǔ)程序的限制的更多討論在附錄?I, 特性限制 里提到。
如20.4節(jié),“存儲(chǔ)子程序和觸發(fā)程序的二進(jìn)制日志功能” 里所說(shuō)的, 存儲(chǔ)子程序的二進(jìn)制日志功能已經(jīng)完成。
存儲(chǔ)程序需要在mysql數(shù)據(jù)庫(kù)中有proc表。這個(gè)表在MySQL 5.1安裝過(guò)程中創(chuàng)建。如果你從早期的版本升級(jí)到MySQL 5.1 ,請(qǐng)確定更新你的授權(quán)表以確保proc表的存在。請(qǐng)參閱2.10.2節(jié) “升級(jí)授權(quán)表” 。
在MySQL 5.1中,授權(quán)系統(tǒng)如下考慮存儲(chǔ)子程序:
·???????? 創(chuàng)建存儲(chǔ)子程序需要CREATE ROUTINE權(quán)限。
·???????? 提醒或移除存儲(chǔ)子程序需要ALTER ROUTINE權(quán)限。這個(gè)權(quán)限自動(dòng)授予子程序的創(chuàng)建者。
·???????? 執(zhí)行子程序需要EXECUTE權(quán)限。然而,這個(gè)權(quán)限自動(dòng)授予 子程序的創(chuàng)建者。同樣,子程序默認(rèn)的SQL SECURITY 特征是DEFINER,它允許用該子程序訪問(wèn)數(shù)據(jù)庫(kù)的用戶與執(zhí)行子程序聯(lián)系到一起。
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 STATUS
20.2.6. CALL語(yǔ)句
20.2.7. BEGIN ... END復(fù)合語(yǔ)句
20.2.8. DECLARE語(yǔ)句
20.2.9. 存儲(chǔ)程序中的變量
20.2.10. 條件和處理程序
20.2.11. 光標(biāo)
20.2.12. 流程控制構(gòu)造
存儲(chǔ)程序和函數(shù)是用CREATE PROCEDURE和CREATE FUNCTION語(yǔ)句創(chuàng)建的子程序。一個(gè)子程序要么是一個(gè)程序要么是一個(gè)函數(shù)。使用CALL語(yǔ)句來(lái)調(diào)用 程序,程序只能用輸出變量傳回值。就像別其它函數(shù)調(diào)用一樣,函數(shù)可以被從語(yǔ)句外調(diào)用(即通過(guò)引用函數(shù)名),函數(shù)能返回標(biāo)量值。 存儲(chǔ)子程序也可以調(diào)用其它存儲(chǔ)子程序。
在MySQL 5.1中,一個(gè)存儲(chǔ)子程序或函數(shù)與特定的數(shù)據(jù)庫(kù)相聯(lián)系。這里有幾個(gè)意思:
·???????? 當(dāng)一個(gè)子程序被調(diào)用時(shí),一個(gè)隱含的USE db_name 被執(zhí)行(當(dāng)子程序終止時(shí)停止執(zhí)行)。存儲(chǔ)子程序內(nèi)的USE語(yǔ)句時(shí)不允許的。
·???????? 你可以使用數(shù)據(jù)庫(kù)名限定子程序名。這可以被用來(lái)引用一個(gè)不在當(dāng)前數(shù)據(jù)庫(kù)中的子程序。比如,要引用一個(gè)與test數(shù)據(jù)庫(kù)關(guān)聯(lián)的存儲(chǔ)程序p或函數(shù)f,你可以說(shuō)CALL test.p()或test.f()。
·???????? 數(shù)據(jù)庫(kù)移除的時(shí)候,與它關(guān)聯(lián)的所有存儲(chǔ)子程序也都被移除。
MySQL 支持非常有用的擴(kuò)展,即它允許在存儲(chǔ)程序中使用常規(guī)的SELECT語(yǔ)句(那就是說(shuō),不使用光標(biāo)或 局部變量)。這個(gè)一個(gè)查詢的結(jié)果包被簡(jiǎn)單地直接送到客戶端。多SELECT語(yǔ)句生成多個(gè)結(jié)果包,所以客戶端必須使用支持多結(jié)果包的MySQL客戶端庫(kù)。這意味這客戶端必須 使用至少MySQL 4.1以來(lái)的近期版本上的客戶端庫(kù)。
下面一節(jié)描述用來(lái)創(chuàng)建,改變,移除和查詢存儲(chǔ)程序和函數(shù)的語(yǔ)法。
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
這些語(yǔ)句創(chuàng)建存儲(chǔ)子程序。要在MySQL 5.1中創(chuàng)建子程序,必須具有CREATE ROUTINE權(quán)限,并且ALTER ROUTINE和EXECUTE權(quán)限被自動(dòng)授予它的創(chuàng)建者。如果二進(jìn)制日志功能被允許,你也可能需要SUPER權(quán)限,請(qǐng)參閱 20.4節(jié),“存儲(chǔ)子程序和觸發(fā)程序的二進(jìn)制日志功能” 。
默認(rèn)地,子程序與當(dāng)前數(shù)據(jù)庫(kù)關(guān)聯(lián)。要明確地把子程序與一個(gè)給定數(shù)據(jù)庫(kù)關(guān)聯(lián)起來(lái),可以在創(chuàng)建子程序的時(shí)候指定其名字為db_name.sp_name。
如果子程序名和內(nèi)建的SQL函數(shù)名一樣,定義子程序時(shí),你需要在這個(gè)名字和隨后括號(hào)中間插入一個(gè)空格,否則發(fā)生語(yǔ)法錯(cuò)誤。當(dāng)你隨后調(diào)用子程序的時(shí)候也要插入。為此,即使有可能出現(xiàn)這種情況,我們還是建議最好避免給你自己的 存儲(chǔ)子程序取與存在的SQL函數(shù)一樣的名字。
由括號(hào)包圍的參數(shù)列必須總是存在。如果沒有參數(shù),也該使用一個(gè)空參數(shù)列()。每個(gè)參數(shù) 默認(rèn)都是一個(gè)IN參數(shù)。要指定為其它參數(shù),可在參數(shù)名之前使用關(guān)鍵詞 OUT或INOUT
注意: 指定參數(shù)為IN, OUT, 或INOUT 只對(duì)PROCEDURE是合法的。(FUNCTION參數(shù)總是被認(rèn)為是IN參數(shù))
RETURNS字句只能對(duì)FUNCTION做指定,對(duì)函數(shù)而言這是強(qiáng)制的。它用來(lái)指定函數(shù)的返回類型,而且函數(shù)體必須包含一個(gè)RETURN value語(yǔ)句。
routine_body 包含合法的SQL過(guò)程語(yǔ)句。可以使用復(fù)合語(yǔ)句語(yǔ)法,請(qǐng)參閱20.2.7節(jié),“BEGIN ... END復(fù)合語(yǔ)句” 。復(fù)合語(yǔ)句可以包含 聲明,循環(huán)和其它控制結(jié)構(gòu)語(yǔ)句。這些語(yǔ)句的語(yǔ)法在本章后免介紹,舉例,請(qǐng)參閱20.2.8節(jié),“DECLARE語(yǔ)句” 和20.2.12節(jié),“流程控制構(gòu)造” 。
CREATE FUNCTION語(yǔ)句被用在更早的MySQL版本上以支持UDF (自定義函數(shù))。請(qǐng)參閱27.2節(jié),“給MySQL添加新函數(shù)” 。 UDF繼續(xù)被支持,即使現(xiàn)在 有了存儲(chǔ)函數(shù)。UDF會(huì)被認(rèn)為一個(gè)外部存儲(chǔ)函數(shù)。然而,不要讓存儲(chǔ)函數(shù)與UDF函數(shù)共享名字空間。
外部存儲(chǔ)程序的框架將在不久的將來(lái)引入。這將允許你用SQL之外的語(yǔ)言編寫存儲(chǔ)程序。最可能的是,第一個(gè)被支持語(yǔ)言是PHP,因?yàn)楹诵腜HP引擎很小,線程安全,且可以被方便地嵌入。因?yàn)榭蚣苁枪_的,它希望許多其它語(yǔ)言也能被支持。
如果程序或線程總是對(duì)同樣的輸入?yún)?shù)產(chǎn)生同樣的結(jié)果,則被認(rèn)為它是“確定的”,否則就是“非確定”的。如果既沒有給定DETERMINISTIC也沒有給定NOT DETERMINISTIC,默認(rèn)的就是NOT DETERMINISTIC。
為進(jìn)行復(fù)制,使用NOW()函數(shù)(或它的同義詞)或
當(dāng)前來(lái)講,DETERMINISTIC特征被接受,但還沒有被優(yōu)化程序所使用。然而如果二進(jìn)制日志功能被允許了,這個(gè)特征影響到MySQL是否會(huì)接受子程序定義。請(qǐng)參閱20.4 節(jié),“存儲(chǔ)子程序和觸發(fā)程序的二進(jìn)制日志功能” 。
一些特征提供子程序使用數(shù)據(jù)的內(nèi)在信息。CONTAINS SQL表示子程序不包含讀或?qū)憯?shù)據(jù)的語(yǔ)句。NO SQL表示子程序不包含SQL語(yǔ)句。READS SQL DATA表示子程序包含讀數(shù)據(jù)的語(yǔ)句,但不包含寫數(shù)據(jù)的語(yǔ)句。MODIFIES SQL DATA表示子程序包含寫數(shù)據(jù)的語(yǔ)句。如果這些特征沒有明確給定,默認(rèn)的是CONTAINS SQL。
SQL SECURITY特征可以用來(lái)指定 子程序該用創(chuàng)建子程序者的許可來(lái)執(zhí)行,還是使用調(diào)用者的許可來(lái)執(zhí)行。默認(rèn)值是DEFINER。在SQL:2003中者是一個(gè)新特性。創(chuàng)建者或調(diào)用者必須由訪問(wèn) 子程序關(guān)聯(lián)的數(shù)據(jù)庫(kù)的許可。在MySQL 5.1中,必須有EXECUTE權(quán)限才能執(zhí)行子程序。必須擁有這個(gè)權(quán)限的用戶要么是定義者,要么是調(diào)用者,這取決于SQL SECURITY特征是如何設(shè)置的。
MySQL存儲(chǔ)sql_mode系統(tǒng)變量設(shè)置,這個(gè)設(shè)置在子程序被創(chuàng)建的時(shí)候起作用,MySQL總是強(qiáng)制使用這個(gè)設(shè)置來(lái)執(zhí)行 子程序。
COMMENT子句是一個(gè)MySQL的擴(kuò)展,它可以被用來(lái)描述 存儲(chǔ)程序。這個(gè)信息被SHOW CREATE PROCEDURE和 SHOW CREATE FUNCTION語(yǔ)句來(lái)顯示。
MySQL允許子程序包含DDL語(yǔ)句,如CREATE和DROP。MySQL也允許存儲(chǔ)程序(但不是 存儲(chǔ)函數(shù))包含SQL 交互語(yǔ)句,如COMMIT。存儲(chǔ)函數(shù)不可以包含那些做明確的和絕對(duì)的提交或者做回滾的語(yǔ)。SQL標(biāo)準(zhǔn)不要求對(duì)這些語(yǔ)句的支持,SQL標(biāo)準(zhǔn)聲明每個(gè)DBMS提供商可以決定是否允許支持這些語(yǔ)句。
存儲(chǔ)子程序不能使用LOAD DATA INFILE。
返回結(jié)果包的語(yǔ)句不能被用在存儲(chǔ)函數(shù)種。這包括不使用INTO給變量讀取 列值的SELECT語(yǔ)句,SHOW 語(yǔ)句,及其它諸如EXPLAIN這樣的語(yǔ)句。對(duì)于可在函數(shù)定義時(shí)間被決定要返回一個(gè)結(jié)果包的語(yǔ)句,發(fā)生一個(gè)允許從函數(shù)錯(cuò)誤返回結(jié)果包的Not(ER_SP_NO_RETSET_IN_FUNC)。對(duì)于只可在運(yùn)行時(shí)決定要返回一個(gè)結(jié)果包的語(yǔ)句, 發(fā)生一個(gè)不能在給定上下文錯(cuò)誤返回結(jié)果包的PROCEDURE %s (ER_SP_BADSELECT)。
下面是一個(gè)使用OUT參數(shù)的簡(jiǎn)單的存儲(chǔ)程序的例子。例子為,在 程序被定義的時(shí)候,用mysql客戶端delimiter命令來(lái)把語(yǔ)句定界符從 ;變?yōu)?/。這就允許用在 程序體中的;定界符被傳遞到服務(wù)器而不是被mysql自己來(lái)解釋。
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)
當(dāng)使用delimiter命令時(shí),你應(yīng)該避免使用反斜杠(‘\’)字符,因?yàn)槟鞘荕ySQL的 轉(zhuǎn)義字符。
下列是一個(gè)例子,一個(gè)采用參數(shù)的函數(shù)使用一個(gè)SQL函數(shù)執(zhí)行一個(gè)操作,并返回結(jié)果:
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)
如果在存儲(chǔ)函數(shù)中的RETURN語(yǔ)句返回一個(gè)類型不同于在函數(shù)的RETURNS子句中指定類型的值,返回值被強(qiáng)制為恰當(dāng)?shù)念愋汀1热?,如果一個(gè)函數(shù)返回一個(gè)ENUM或SET值,但是RETURN語(yǔ)句返回一個(gè)整數(shù),對(duì)于SET成員集的相應(yīng)的ENUM成員,從函數(shù)返回的值是字符串。
ALTER {PROCEDURE | FUNCTION} sp_name [characteristic ...]
?
characteristic:
??? { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
? | SQL SECURITY { DEFINER | INVOKER }
? | COMMENT 'string'
這個(gè)語(yǔ)句可以被用來(lái)改變一個(gè)存儲(chǔ)程序或函數(shù)的特征。在MySQL 5.1中,你必須用ALTER ROUTINE權(quán)限才可用此子程序。這個(gè)權(quán)限被自動(dòng)授予子程序的創(chuàng)建者。如20.4節(jié),“存儲(chǔ)子程序和觸發(fā)程序的二進(jìn)制日志功能”中所述, 如果二進(jìn)制日志功能被允許了,你可能也需要SUPER權(quán)限。
在ALTER PROCEDURE和ALTER FUNCTION語(yǔ)句中,可以指定超過(guò)一個(gè)的改變。
DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name
這個(gè)語(yǔ)句被用來(lái)移除一個(gè)存儲(chǔ)程序或函數(shù)。即,從服務(wù)器移除一個(gè)制定的子程序。在MySQL 5.1中,你必須有ALTER ROUTINE權(quán)限才可用此子程序。這個(gè)權(quán)限被自動(dòng)授予子程序的創(chuàng)建者。
IF EXISTS 子句是一個(gè)MySQL的擴(kuò)展。如果程序或函數(shù)不存儲(chǔ),它防止發(fā)生錯(cuò)誤。產(chǎn)生一個(gè)可以用SHOW WARNINGS查看的警告。
SHOW CREATE {PROCEDURE | FUNCTION} sp_name
這個(gè)語(yǔ)句是一個(gè)MySQL的擴(kuò)展。類似于SHOW CREATE TABLE,它返回一個(gè)可用來(lái)重新創(chuàng)建已命名 子程序的確切字符串。
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,'!')
SHOW {PROCEDURE | FUNCTION} STATUS [LIKE 'pattern']
這個(gè)語(yǔ)句是一個(gè)MySQL的擴(kuò)展。它返回子程序的特征,如數(shù)據(jù)庫(kù),名字,類型,創(chuàng)建者及創(chuàng)建和修改日期。如果沒有指定樣式,根據(jù)你使用的語(yǔ)句,所有 存儲(chǔ)程序和所有存儲(chǔ)函數(shù)的信息都被列出。
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表獲得有關(guān)存儲(chǔ)子程序的信息。請(qǐng)參閱23.1.14節(jié),“INFORMATION_SCHEMA ROUTINES 表” 。
CALL sp_name([parameter[,...]])
CALL語(yǔ)句調(diào)用一個(gè)先前用CREATE PROCEDURE創(chuàng)建的程序。
CALL語(yǔ)句可以用 聲明為OUT或的INOUT參數(shù)的參數(shù)給它的調(diào)用者傳回值。它也“返回”受影響的行數(shù),客戶端程序可以在SQL級(jí)別通過(guò)調(diào)用ROW_COUNT()函數(shù)獲得這個(gè)數(shù),從C中是調(diào)用the mysql_affected_rows() C API函數(shù)來(lái)獲得。
[begin_label:] BEGIN
??? [statement_list]
END [end_label]
存儲(chǔ)子程序可以使用BEGIN ... END復(fù)合語(yǔ)句來(lái)包含多個(gè)語(yǔ)句。statement_list 代表一個(gè)或多個(gè)語(yǔ)句的列表。statement_list之內(nèi)每個(gè)語(yǔ)句都必須用分號(hào)(;)來(lái)結(jié)尾。
復(fù)合語(yǔ)句可以被標(biāo)記。除非begin_label存在,否則end_label不能被給出,并且如果二者都存在,他們必須是同樣的。
請(qǐng)注意,可選的[NOT] ATOMIC子句現(xiàn)在還不被支持。這意味著在指令塊的開始沒有交互的存儲(chǔ)點(diǎn)被設(shè)置,并且在上下文中用到的BEGIN子句對(duì)當(dāng)前交互動(dòng)作沒有影響。
使用多重語(yǔ)句需要客戶端能發(fā)送包含語(yǔ)句定界符;的查詢字符串。這個(gè)符號(hào)在命令行客戶端被用delimiter命令來(lái)處理。改變查詢結(jié)尾定界符;(比如改變?yōu)?/)使得; 可被用在子程序體中。
DECLARE語(yǔ)句被用來(lái)把不同項(xiàng)目局域到一個(gè) 子程序:局部變量(請(qǐng)參閱20.2.9節(jié),“存儲(chǔ)程序中的變量”),條件和 處理程序(請(qǐng)參閱20.2.10節(jié),“條件和處理程序”) 及光標(biāo)(請(qǐng)參閱20.2.11節(jié),“光標(biāo)”)。SIGNAL和RESIGNAL語(yǔ)句當(dāng)前還不被支持。
DECLARE僅被用在BEGIN ... END復(fù)合語(yǔ)句里,并且必須在復(fù)合語(yǔ)句的開頭,在任何其它語(yǔ)句之前。
光標(biāo)必須在聲明處理程序之前被聲明,并且變量和條件必須在聲明光標(biāo)或處理程序之前被聲明。
20.2.9.1. DECLARE局部變量
20.2.9.2. 變量SET語(yǔ)句
20.2.9.3. SELECT ... INTO語(yǔ)句
你可以在子程序中聲明并使用變量。
DECLARE var_name[,...] type [DEFAULT value]
這個(gè)語(yǔ)句被用來(lái)聲明局部變量。要給變量提供一個(gè)默認(rèn)值,請(qǐng)包含一個(gè)DEFAULT子句。值可以被指定為一個(gè)表達(dá)式,不需要為一個(gè)常數(shù)。如果沒有DEFAULT子句,初始值為NULL。
局部變量的作用范圍在它被聲明的BEGIN ... END塊內(nèi)。它可以被用在嵌套的塊中,除了那些用相同名字 聲明變量的塊。
SET var_name = expr [, var_name = expr] ...
在存儲(chǔ)程序中的SET語(yǔ)句是一般SET語(yǔ)句的擴(kuò)展版本。被參考變量可能是子程序內(nèi)聲明的變量,或者是全局服務(wù)器變量。
在存儲(chǔ)程序中的SET語(yǔ)句作為預(yù)先存在的SET語(yǔ)法的一部分來(lái)實(shí)現(xiàn)。這允許SET a=x, b=y, ...這樣的擴(kuò)展語(yǔ)法。其中不同的變量類型(局域 聲明變量及全局和集體變量)可以被混合起來(lái)。這也允許把局部變量和一些只對(duì)系統(tǒng)變量有意義的選項(xiàng)合并起來(lái)。在那種情況下,此選項(xiàng)被識(shí)別,但是被忽略了。
SELECT col_name[,...] INTO var_name[,...] table_expr
這個(gè)SELECT語(yǔ)法把選定的列直接存儲(chǔ)到變量。因此,只有單一的行可以被取回。
SELECT id,data INTO x,y FROM test.t1 LIMIT 1;
注意,用戶變量名在MySQL 5.1中是對(duì)大小寫不敏感的。請(qǐng)參閱9.3節(jié),“用戶變量”。
重要: SQL變量名不能和列名一樣。如果SELECT ... INTO這樣的SQL語(yǔ)句包含一個(gè)對(duì)列的參考,并包含一個(gè)與列相同名字的 局部變量,MySQL當(dāng)前把參考解釋為一個(gè)變量的名字。例如,在下面的語(yǔ)句中,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;
當(dāng)這個(gè)程序被調(diào)用的時(shí)候,無(wú)論table.xname列的值是什么,變量newname將返回值‘bob’。
請(qǐng)參閱I.1節(jié),“存儲(chǔ)子程序和觸發(fā)程序的限制” 。
20.2.10.1. DECLARE條件
20.2.10.2. DECLARE處理程序
特定條件需要特定處理。這些條件可以聯(lián)系到錯(cuò)誤,以及子程序中的一般流程控制。
DECLARE condition_name CONDITION FOR condition_value
?
condition_value:
??? SQLSTATE [VALUE] sqlstate_value
? | mysql_error_code
這個(gè)語(yǔ)句指定需要特殊處理的條件。它將一個(gè)名字和指定的錯(cuò)誤條件關(guān)聯(lián)起來(lái)。這個(gè)名字可以隨后被用在DECLARE HANDLER語(yǔ)句中。請(qǐng)參閱20.2.10.2節(jié),“DECLARE處理程序” 。
除了SQLSTATE值,也支持MySQL錯(cuò)誤代碼。
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
這個(gè)語(yǔ)句指定每個(gè)可以處理一個(gè)或多個(gè)條件的處理程序。如果產(chǎn)生一個(gè)或多個(gè)條件,指定的語(yǔ)句被執(zhí)行。
對(duì)一個(gè)CONTINUE處理程序,當(dāng)前子程序的執(zhí)行在執(zhí)行 處理程序語(yǔ)句之后繼續(xù)。對(duì)于EXIT處理程序,當(dāng)前BEGIN...END復(fù)合語(yǔ)句的執(zhí)行被終止。UNDO 處理程序類型語(yǔ)句還不被支持。
·???????? SQLWARNING是對(duì)所有以01開頭的SQLSTATE代碼的速記。
·???????? NOT FOUND是對(duì)所有以02開頭的SQLSTATE代碼的速記。
·???????? SQLEXCEPTION是對(duì)所有沒有被SQLWARNING或NOT FOUND捕獲的SQLSTATE代碼的速記。
除了SQLSTATE值,MySQL錯(cuò)誤代碼也不被支持。
例如:
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被執(zhí)行到程序的末尾。如果DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; 這一行不在,第二個(gè)INSERT因PRIMARY KEY強(qiáng)制而失敗之后,MySQL可能已經(jīng)采取 默認(rèn)(EXIT)路徑,并且SELECT @x可能已經(jīng)返回2。
20.2.11.1.聲明光標(biāo)
20.2.11.2. 光標(biāo)OPEN語(yǔ)句
20.2.11.3. 光標(biāo)FETCH語(yǔ)句
20.2.11.4. 光標(biāo)CLOSE語(yǔ)句
簡(jiǎn)單光標(biāo)在存儲(chǔ)程序和函數(shù)內(nèi)被支持。語(yǔ)法如同在嵌入的SQL中。光標(biāo)當(dāng)前是不敏感的,只讀的及不滾動(dòng)的。不敏感意為服務(wù)器可以活不可以復(fù)制它的結(jié)果表。
光標(biāo)必須在聲明處理程序之前被聲明,并且變量和條件必須在聲明光標(biāo)或處理程序之前被聲明。
例如:
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
DECLARE cursor_name CURSOR FOR select_statement
這個(gè)語(yǔ)句聲明一個(gè)光標(biāo)。也可以在子程序中定義多個(gè)光標(biāo),但是一個(gè)塊中的每一個(gè)光標(biāo)必須有唯一的名字。
SELECT語(yǔ)句不能有INTO子句。
20.2.12.1. IF語(yǔ)句
20.2.12.2. CASE語(yǔ)句
20.2.12.3. LOOP語(yǔ)句
20.2.12.4. LEAVE語(yǔ)句
20.2.12.5. ITERATE語(yǔ)句
20.2.12.6. REPEAT語(yǔ)句
20.2.12.7. WHILE語(yǔ)句
IF, CASE, LOOP, WHILE, ITERATE, 及 LEAVE 構(gòu)造被完全實(shí)現(xiàn)。
這些構(gòu)造可能每個(gè)包含要么一個(gè)單獨(dú)語(yǔ)句,要么是使用BEGIN ... END復(fù)合語(yǔ)句的一塊語(yǔ)句。構(gòu)造可以被嵌套。
目前還不支持FOR循環(huán)。
IF search_condition THEN statement_list
??? [ELSEIF search_condition THEN statement_list] ...
??? [ELSE statement_list]
END IF
IF實(shí)現(xiàn)了一個(gè)基本的條件構(gòu)造。如果search_condition求值為真,相應(yīng)的SQL語(yǔ)句列表被執(zhí)行。如果沒有search_condition匹配,在ELSE子句里的語(yǔ)句列表被執(zhí)行。statement_list可以包括一個(gè)或多個(gè)語(yǔ)句。
請(qǐng)注意,也有一個(gè)IF() 函數(shù),它不同于這里描述的IF語(yǔ)句。請(qǐng)參閱12.2節(jié),“控制流程函數(shù)”。
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
存儲(chǔ)程序的CASE語(yǔ)句實(shí)現(xiàn)一個(gè)復(fù)雜的條件構(gòu)造。如果search_condition 求值為真,相應(yīng)的SQL被執(zhí)行。如果沒有搜索條件匹配,在ELSE子句里的語(yǔ)句被執(zhí)行。
注意:這里介紹的用在 存儲(chǔ)程序里的CASE語(yǔ)句與12.2節(jié),“控制流程函數(shù)”里描述的SQL CASE表達(dá)式的CASE語(yǔ)句有輕微不同。這里的CASE語(yǔ)句不能有ELSE NULL子句,并且用END CASE替代END來(lái)終止。
[begin_label:] LOOP
??? statement_list
END LOOP [end_label]
LOOP允許某特定語(yǔ)句或語(yǔ)句群的重復(fù)執(zhí)行,實(shí)現(xiàn)一個(gè)簡(jiǎn)單的循環(huán)構(gòu)造。在循環(huán)內(nèi)的語(yǔ)句一直重復(fù)直循環(huán)被退出,退出通常伴隨著一個(gè)LEAVE 語(yǔ)句。
LOOP語(yǔ)句可以被標(biāo)注。除非begin_label存在,否則end_label不能被給出,并且如果兩者都出現(xiàn),它們必須是同樣的。
ITERATE label
ITERATE只可以出現(xiàn)在LOOP, REPEAT, 和WHILE語(yǔ)句內(nèi)。ITERATE意思為:“再次循環(huán)?!?
例如:
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
[begin_label:] REPEAT
??? statement_list
UNTIL search_condition
END REPEAT [end_label]
REPEAT語(yǔ)句內(nèi)的語(yǔ)句或語(yǔ)句群被重復(fù),直至search_condition 為真。
REPEAT 語(yǔ)句可以被標(biāo)注。 除非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)
[begin_label:] WHILE search_condition DO
??? statement_list
END WHILE [end_label]
WHILE語(yǔ)句內(nèi)的語(yǔ)句或語(yǔ)句群被重復(fù),直至search_condition 為真。
WHILE語(yǔ)句可以被標(biāo)注。 除非begin_label也存在,end_label才能被用,如果兩者都存在,它們必須是一樣的。
例如:
CREATE PROCEDURE dowhile()
BEGIN
? DECLARE v1 INT DEFAULT 5;
?
? WHILE v1 > 0 DO
??? ...
??? SET v1 = v1 - 1;
? END WHILE;
END
是的,在存儲(chǔ)程序和函數(shù)中被執(zhí)行標(biāo)準(zhǔn)行為被從主MySQL服務(wù)器復(fù)制到從服務(wù)器。有少數(shù)限制,它們?cè)?0.4節(jié),“存儲(chǔ)子程序和 觸發(fā)程序二進(jìn)制日志功能”中詳述。
是的,通過(guò)一般DDL語(yǔ)句執(zhí)行的存儲(chǔ)程序和函數(shù),其在主服務(wù)器上的創(chuàng)建被復(fù)制到從服務(wù)器,所以目標(biāo)將存在兩個(gè)服務(wù)器上。對(duì)存儲(chǔ)程序和函數(shù)的ALTER 和DROP語(yǔ)句也被復(fù)制。
MySQL紀(jì)錄每個(gè)發(fā)生在存儲(chǔ)程序和函數(shù)里的DML事件,并復(fù)制這些單獨(dú)的行為到從服務(wù)器。執(zhí)行存儲(chǔ)程序和函數(shù)的切實(shí)調(diào)用不被復(fù)制。
是的,因?yàn)橐粋€(gè)從服務(wù)器有權(quán)限來(lái)執(zhí)行任何讀自主服務(wù)器的二進(jìn)制日志的語(yǔ)句,指定的安全約束因與復(fù)制一起使用的存儲(chǔ)程序和函數(shù)而存在。如果復(fù)制或二進(jìn)制日志大體上是激活的(為point-in-time恢復(fù)的目的),那么MySQL DBA 有兩個(gè)安全選項(xiàng)可選:
嵌入到存儲(chǔ)程序中的不確定(隨機(jī))或時(shí)基行不能適當(dāng)?shù)貜?fù)制。隨機(jī)產(chǎn)生的結(jié)果,僅因其本性,是你可預(yù)測(cè)的和不能被確實(shí)克隆的。因此,復(fù)制到從服務(wù)器的隨機(jī)行為將不會(huì)鏡像那些產(chǎn)生在主服務(wù)器上的。注意, 聲明存儲(chǔ)程序或函數(shù)為DETERMINISTIC或者在log_bin_trust_routine_creators中設(shè)置系統(tǒng)變量為0 將會(huì)允許隨即值操作被調(diào)用。
此外,時(shí)基行為不能在從服務(wù)器上重新產(chǎn)生,因?yàn)樵诖鎯?chǔ)程序中通過(guò)對(duì)復(fù)制使用的二進(jìn)制日志來(lái)計(jì)時(shí)這樣的時(shí)基行為是不可重新產(chǎn)生的,因?yàn)樵摱M(jìn)制日志僅紀(jì)錄DML事件且不包括計(jì)時(shí)約束。
最后,在大型DML行為(如大批插入)中非交互表發(fā)生錯(cuò)誤,該非交互表可能經(jīng)歷復(fù)制,在復(fù)制版的非交互表中主服務(wù)器可以被部分地從DML行為更新。但是因?yàn)榘l(fā)生的那個(gè)錯(cuò)誤,對(duì)從服務(wù)器沒有更新。 對(duì)函數(shù)的DML行為,工作區(qū)將被用IGNORE關(guān)鍵詞來(lái)執(zhí)行,以便于在主服務(wù)器上導(dǎo)致錯(cuò)誤的更新被忽略,并且不會(huì)導(dǎo)致錯(cuò)誤的更新被復(fù)制到從服務(wù)器。
?
影響復(fù)制的同一限制會(huì)影響point-in-time恢復(fù)。
將來(lái)發(fā)行的MySQL預(yù)期有一個(gè)功能去選擇復(fù)制該如何被處理:
MySQL 5.1中的觸發(fā)程序和復(fù)制象在大多數(shù)其它數(shù)據(jù)庫(kù)引擎中一樣工作,在那些引擎中,通過(guò)觸發(fā)程序在主服務(wù)器上執(zhí)行的行為不被復(fù)制到從服務(wù)器。取而代之的是,位于主MySQL服務(wù)器的表中的 觸發(fā)程序需要在那些存在于任何MySQL從服務(wù)器上的表內(nèi)被創(chuàng)建,以便于觸發(fā)程序可以也可以在從服務(wù)器上被激活。
?
首先,主服務(wù)器上的觸發(fā)程序必須在從服務(wù)器上重建。一旦重建了,復(fù)制流程就象其它參與到復(fù)制中的標(biāo)準(zhǔn)DML語(yǔ)句一樣工作。例如:考慮一個(gè)已經(jīng)插入觸發(fā)程序AFTER的EMP表,它位于主MySQL服務(wù)器上。同樣的EMP表和AFTER插入 觸發(fā)程序也存在于從服務(wù)器上。復(fù)制流程可能是:
1.??? 對(duì)EMP做一個(gè)INSERT語(yǔ)句。
2.???EMP上的AFTER觸發(fā)程序激活。
3.??? INSERT語(yǔ)句被寫進(jìn)二進(jìn)制日志。
4.??? 從服務(wù)器上的復(fù)制拾起INSERT語(yǔ)句給EMP表,并在從服務(wù)器上執(zhí)行它。
5.??? 位于從服務(wù)器EMP上的AFTER觸發(fā)程序激活。
,這一節(jié)介紹MySQL 5.1如何考慮二進(jìn)制日志功能來(lái)處理存儲(chǔ)子程序(程序和函數(shù)) 。這一節(jié)也適用于觸發(fā)程序。
二進(jìn)制日志包含修改數(shù)據(jù)庫(kù)內(nèi)容的SQL語(yǔ)句的信息。這個(gè)信息以描述修改的事件的形式保存起來(lái)。
二進(jìn)制日志有兩個(gè)重要目的:
·???????? 復(fù)制的基礎(chǔ)是主服務(wù)器發(fā)送包含在二進(jìn)制日志里的事件到從服務(wù)器,從服務(wù)器執(zhí)行這些事件來(lái)造成與對(duì)主服務(wù)器造成的同樣的數(shù)據(jù)改變,請(qǐng)參閱6.2節(jié),“復(fù)制概述”。
·???????? 特定的數(shù)據(jù)恢復(fù)操作許要使用二進(jìn)制日志。備份的文件被恢復(fù)之后,備份后紀(jì)錄的二進(jìn)制日志里的事件被重新執(zhí)行。這些事件把數(shù)據(jù)庫(kù)帶從備份點(diǎn)的日子帶到當(dāng)前。請(qǐng)參閱5.9.2.2節(jié),“使用備份恢復(fù)”。
MySQL中,以存儲(chǔ)子程序的二進(jìn)制日志功能引發(fā)了很多問(wèn)題,這些在下面討論中列出,作為參考信息。
除了要另外注意的之外,這些談?wù)摷僭O(shè)你已經(jīng)通過(guò)用--log-bin選項(xiàng)啟動(dòng)服務(wù)器允許了二進(jìn)制日志功能。(如果二進(jìn)制日志功能不被允許,復(fù)制將不可能,為數(shù)據(jù)恢復(fù)的二進(jìn)制日志也不存在。)請(qǐng)參閱5.11.3節(jié),“二進(jìn)制日志”。
對(duì)存儲(chǔ)子程序語(yǔ)句的二進(jìn)制日志功能的特征在下面列表中描述。一些條目指出你應(yīng)該注意到的問(wèn)題。但是在一些情況下,有你可以更改的婦五七設(shè)置或你可以用來(lái)處理它們的工作區(qū)。
·???????? CREATE PROCEDURE, CREATE FUNCTION, ALTER PROCEDURE,和ALTER FUNCTION 語(yǔ)句被寫進(jìn)二進(jìn)制日志,CALL, DROP PROCEDURE, 和DROP FUNCTION 也一樣。
盡管如此,對(duì)復(fù)制有一個(gè)安全暗示:要?jiǎng)?chuàng)建一個(gè)子程序,用戶必須有CREATE ROUTINE權(quán)限,但有這個(gè)權(quán)限的用戶不能寫一個(gè) 子程序在從服務(wù)器上執(zhí)行任何操作。因?yàn)樵趶姆?wù)器上的SQL線程用完全權(quán)限來(lái)運(yùn)行。例如,如果主服務(wù)器和從服務(wù)器分別有服務(wù)器ID值1和2,在主服務(wù)器上的用戶可能創(chuàng)建并調(diào)用如下一個(gè) 程序:
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語(yǔ)句將被寫進(jìn)二進(jìn)制日志,所以從服務(wù)器將執(zhí)行它們。因?yàn)閺腟QL線程有完全權(quán)限,它將移除accounting數(shù)據(jù)庫(kù)。
要使允許二進(jìn)制日志功能的服務(wù)器避免這個(gè)危險(xiǎn),MySQL 5.1已經(jīng)要求 存儲(chǔ)程序和函數(shù)的創(chuàng)建者除了通常需要的CREATE ROUTINE的權(quán)限外,還必須有SUPER 權(quán)限。類似地,要使用ALTER PROCEDURE或ALTER FUNCTION,除了ALTER ROUTINE權(quán)限外你必須有SUPER權(quán)限。沒有SUPER權(quán)限,將會(huì)發(fā)生一個(gè)錯(cuò)誤:
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)
你可能不想強(qiáng)制要求子程序創(chuàng)建者必須有SUPER權(quán)限。例如,你系統(tǒng)上所有有CREATE ROUTINE權(quán)限的用戶可能是有經(jīng)驗(yàn)的應(yīng)用程序開發(fā)者。要禁止掉對(duì)SUPER權(quán)限的要求,設(shè)置log_bin_trust_routine_creators 全局系統(tǒng)變量為1。默認(rèn)地,這個(gè)變量值為0,但你可以象這樣改變這樣:
mysql> SET GLOBAL log_bin_trust_routine_creators = 1;
你也可以在啟動(dòng)服務(wù)器之時(shí)用--log-bin-trust-routine-creators選項(xiàng)來(lái)設(shè)置允許這個(gè)變量。
如果二進(jìn)制日志功能不被允許,log_bin_trust_routine_creators 沒有被用上,子程序創(chuàng)建需要SUPER權(quán)限。
·???????? 一個(gè)執(zhí)行更新的非確定子程序是不可重復(fù)的,它能有兩個(gè)不如意的影響:
o??????? 它會(huì)使得從服務(wù)器不同于主服務(wù)器。
-??????? 恢復(fù)的數(shù)據(jù)與原始數(shù)據(jù)不同。
要解決這些問(wèn)題,MySQL強(qiáng)制做下面要求:在主服務(wù)器上,除非子程序被聲明為確定性的或者不更改數(shù)據(jù),否則創(chuàng)建或者替換子程序?qū)⒈痪芙^。這意味著當(dāng)你創(chuàng)建一個(gè)子程序的時(shí)候,你必須要么聲明它是確定性的,要么它不改變數(shù)據(jù)。兩套子程序特征在這里適用:
-??????? DETERMINISTIC和NOT DETERMINISTIC指出一個(gè)子程序是否對(duì)給定的輸入總是產(chǎn)生同樣的結(jié)果。如果沒有給定任一特征,默認(rèn)是NOT DETERMINISTIC,所以你必須明確指定DETERMINISTIC來(lái) 聲明一個(gè) 子程序是確定性的。
使用NOW() 函數(shù)(或它的同義)或者RAND() 函數(shù)不是必要地使也一個(gè)子程序非確定性。對(duì)NOW()而言,二進(jìn)制日志包括時(shí)間戳并正確復(fù)制。RAND()只要在一個(gè) 子程序內(nèi)被調(diào)用一次也可以正確復(fù)制。(你可以認(rèn)為子程序執(zhí)行時(shí)間戳和隨機(jī)數(shù)種子作為毫無(wú)疑問(wèn)地輸入,它們?cè)谥鞣?wù)器和從服務(wù)器上是一樣的。)
-??????? CONTAINS SQL, NO SQL, READS SQL DATA, 和 MODIFIES SQL數(shù)據(jù)提供子程序是讀還是寫數(shù)據(jù)的信息。無(wú)論NO SQL 還是READS SQL DATA i都指出,子程序沒有改變數(shù)據(jù),但你必須明白地指明這些中的一個(gè),因?yàn)槿绻魏芜@些特征沒有被給出, 默認(rèn)的特征是CONTAINS SQL。
默認(rèn)地,要一個(gè)CREATE PROCEDURE 或 CREATE FUNCTION 語(yǔ)句被接受,DETERMINISTIC 或 NO SQL與READS SQL DATA 中的一個(gè)必須明白地指定,否則會(huì)產(chǎn)生如下錯(cuò)誤:
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)
如果設(shè)置log_bin_trust_routine_creators 為1, 移除對(duì)子程序必須是確定的或不修改數(shù)據(jù)的要求。
注意,子程序本性的評(píng)估是基于創(chuàng)建者的“誠(chéng)實(shí)度” :MySQL不檢查聲明為確定性的子程序是否不含產(chǎn)生非確定性結(jié)果的語(yǔ)句。
·???????? 如果子程序返回?zé)o錯(cuò),CALL語(yǔ)句被寫進(jìn)二進(jìn)制日志,否則就不寫。當(dāng)一個(gè)子程序修改數(shù)據(jù)失敗了,你會(huì)得到這樣的警告:
·??????????????? 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
這個(gè)記日志行為潛在地導(dǎo)致問(wèn)題.如果一個(gè)子程序部分地修改一個(gè)非交互表(比如一個(gè)MyISAM表able)并且返回一個(gè)錯(cuò)誤,二進(jìn)制日志將反映這些變化。要防止這種情況,你應(yīng)該在 子程序中使用交互表并且在交互動(dòng)作內(nèi)修改表。
在一個(gè)子程序內(nèi),如果你在INSERT, DELETE, 或者UPDATE里使用IGNORE關(guān)鍵詞來(lái)忽略錯(cuò)誤,可能發(fā)生一個(gè)部分更新,但沒有錯(cuò)誤產(chǎn)生。這樣的語(yǔ)句被記錄日志,且正常復(fù)制。
·???????? 如果一個(gè)存儲(chǔ)函數(shù)在一個(gè)如SELECT這樣不修改數(shù)據(jù)的語(yǔ)句內(nèi)被調(diào)用,即使函數(shù)本身更改數(shù)據(jù),函數(shù)的執(zhí)行也將不被寫進(jìn)二進(jìn)制日志里。這個(gè)記錄日志的行為潛在地導(dǎo)致問(wèn)題。假設(shè)函數(shù)myfunc()如下定義:
·??????????????? CREATE FUNCTION myfunc () RETURNS INT
·??????????????? BEGIN
·??????????????? ??INSERT INTO t (i) VALUES(1);
·??????????????? ??RETURN 0;
·??????????????? END;
按照上面定義,下面的語(yǔ)句修改表t,因?yàn)閙yfunc()修改表t, 但是語(yǔ)句不被寫進(jìn)二進(jìn)制日志,因?yàn)樗且粋€(gè)SELECT語(yǔ)句:
SELECT myfunc();
對(duì)這個(gè)問(wèn)題的工作區(qū)將調(diào)用在做更新的語(yǔ)句里做更新的函數(shù)。注意,雖然DO語(yǔ)句有時(shí)為了其估算表達(dá)式的副效應(yīng)而被執(zhí)行,DO在這里不是一個(gè)工作區(qū),因?yàn)樗槐粚戇M(jìn)二進(jìn)制日志。
·???????? 在一個(gè)子程序內(nèi)執(zhí)行的語(yǔ)句不被寫進(jìn)二進(jìn)制日志。假如你發(fā)布下列語(yǔ)句:
·??????????????? CREATE PROCEDURE mysp INSERT INTO t VALUES(1);
·??????????????? CALL mysp;
對(duì)于這個(gè)例子來(lái)說(shuō),CREATE PROCEDURE 和CALL語(yǔ)句出現(xiàn)在二進(jìn)制日志里,但I(xiàn)NSERT語(yǔ)句并未出現(xiàn)。
·???????? 在從服務(wù)器上,當(dāng)決定復(fù)制哪個(gè)來(lái)自主服務(wù)器的事件時(shí),下列限制被應(yīng)用:--replicate-*-table規(guī)則不適用于CALL語(yǔ)句或子程序內(nèi)的語(yǔ)句:在這些情況下,總是返回“復(fù)制!”
觸發(fā)程序類似于存儲(chǔ)函數(shù),所以前述的評(píng)論也適用于觸發(fā)程序,除了下列情況: CREATE TRIGGER沒有可選的DETERMINISTIC特征,所以觸發(fā)程序被假定為總是確定性的。然而,這個(gè)假設(shè)在一些情況下是非法的。比如,UUID()函數(shù)是非確定性的(不能復(fù)制)。你應(yīng)該小心在 觸發(fā)程序中使用這個(gè)函數(shù)。
觸發(fā)程序目前不能更新表,但是在將來(lái)會(huì)支持。因?yàn)檫@個(gè)原因,如果你沒有SUPER權(quán)限且log_bin_trust_routine_creators 被設(shè)為0,得到的錯(cuò)誤信息類似于存儲(chǔ)子程序與CREATE TRIGGER產(chǎn)生的錯(cuò)誤信息。
在本節(jié)中敘述的問(wèn)題來(lái)自發(fā)生在SQL語(yǔ)句級(jí)別的二進(jìn)制日志記錄的事實(shí)。未來(lái)發(fā)行的MySQL期望能實(shí)現(xiàn)行級(jí)的二進(jìn)制日志記錄,記錄發(fā)生在更 細(xì)致的級(jí)別并且指出哪個(gè)改變作為執(zhí)行SQL的結(jié)果對(duì)單個(gè)記錄而做。
這是MySQL參考手冊(cè)的翻譯版本,關(guān)于MySQL參考手冊(cè),請(qǐng)?jiān)L問(wèn)dev.mysql.com。原始參考手冊(cè)為英文版,與英文版參考手冊(cè)相比,本翻譯版可能不是最新的。
?
?