存储程序(1)——MYSQL
MySQL支持把幾種對(duì)象存放在服務(wù)器端供以后使用。這幾種對(duì)象有一些可以根據(jù)情況通過程序代碼調(diào)用,有一些會(huì)在數(shù)據(jù)表被修改時(shí)自動(dòng)執(zhí)行,還有一些可以在預(yù)定時(shí)刻自動(dòng)執(zhí)行。它們包括以下幾種:
1.存儲(chǔ)函數(shù)(stored function)。返回一個(gè)計(jì)算結(jié)果,該結(jié)果可以用在表達(dá)式里。
2.存儲(chǔ)過程(stored procedure)。不直接返回一個(gè)結(jié)果,但可以用來完成一般的運(yùn)算或是生成一個(gè)結(jié)果集并傳遞回客戶。
3.觸發(fā)器(trigger)。與數(shù)據(jù)表相關(guān)聯(lián),當(dāng)那個(gè)數(shù)據(jù)表被工NSERT、DELETE或UPDATE語句修改時(shí),觸發(fā)器將自動(dòng)執(zhí)行。
4.事件(event)。根據(jù)時(shí)間表在預(yù)定時(shí)刻自動(dòng)執(zhí)行。
MySQL對(duì)存儲(chǔ)函數(shù)和存儲(chǔ)過程的支持始于5.0.0版本,對(duì)觸發(fā)器和事件的支持分別始于5.0.2版本和5.1.6版本。存儲(chǔ)程序有以下優(yōu)點(diǎn)和能力:
1.存儲(chǔ)程序?qū)ο蟮目蓤?zhí)行部分可以用復(fù)合語句來編寫,復(fù)合語句對(duì)SQL語法進(jìn)行了擴(kuò)展,可以包括代碼塊、循環(huán)和條件語句。
2.存儲(chǔ)程序都被保存在服務(wù)器端,定義它們所需要的代碼只需在它們被創(chuàng)建時(shí)通過網(wǎng)絡(luò)傳遞一次,而不是每次執(zhí)行都要傳遞一次。這大大減少了開銷。
3.它們可以把復(fù)雜的計(jì)算封裝為程序單元,而你可以簡(jiǎn)單地通過程序單元的名字來調(diào)用它們。你甚至可以把一組存儲(chǔ)程序打包為一個(gè)“函數(shù)庫”供其他應(yīng)用程序調(diào)用。
4.它們提供了一種錯(cuò)誤處理機(jī)制。
5.它們可以提高數(shù)據(jù)庫的安全性。你可以通過選擇存儲(chǔ)程序執(zhí)行時(shí)所需的權(quán)限下來對(duì)敏感數(shù)據(jù)的訪問情況進(jìn)行限制和調(diào)控。
存儲(chǔ)程序。泛指各種類型的存儲(chǔ)對(duì)象(存儲(chǔ)函數(shù)、存儲(chǔ)過程、觸發(fā)器、事件)。存儲(chǔ)例程(stored routine ),特指存儲(chǔ)函數(shù)和存儲(chǔ)過程。這兩種對(duì)象的定義語法很相似,所以很自然地把它們放在一起討論。在開始討論各種類型的存儲(chǔ)程序之前,我們首先學(xué)習(xí)一下:復(fù)合語句。
1.復(fù)合語句和語句分隔符
簡(jiǎn)單的存儲(chǔ)程序只包含一條SQL語句,在編寫時(shí)不需要特殊對(duì)待。下面的存儲(chǔ)過程使用了一條SELECT語句來列出sampdb數(shù)據(jù)庫里的數(shù)據(jù)表的名字:
PROCEDURE sampdb_tables() SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=’sampdb’ ORDER BY TABLE_NAME;不過,存儲(chǔ)程序并非只能包含一條簡(jiǎn)單的SQL語句。它們可以包含多條SQL語句,可以使用局部變量、條件語句、循環(huán)和嵌套語句塊等多種語法構(gòu)造。要使用這些構(gòu)造編寫存儲(chǔ)程序,就需要用到復(fù)合語句。復(fù)合語句由BEGIN開頭,END結(jié)束,在它們之間可以寫出任意數(shù)量的語句,這些語句構(gòu)成了一個(gè)語句塊。下面的存儲(chǔ)過程將顯示一條歡迎消息,其中有你的用戶名;如果你是一位匿名用戶,用戶名將是“earthing":
CREATE PROCEDURE greetings() BEGIN#77=16 for username+60 for hostname+1 for '@'DECLARE user CHAR(77) CHARACTER SET utf8;SET user = (SELECT CURRENT USER());IF INSTR(user,’@’)>0 THEN#返回字符串在某一個(gè)字段的內(nèi)容中的位置, 沒有找到字符串返回0,否則返回位置(從1開始)SET user=SUBSTRING_INDEX(user, '@', 1);END IF;IF user = '' THENSET user='earthling';END IF;SELECT CONCAT('Greetings,',user, '!') AS greeting; END;在使用復(fù)合語句時(shí),必須考慮和解決這樣一個(gè)問題:復(fù)合語句塊里的語句必須以分號(hào)(;)彼此隔開,但因?yàn)榉痔?hào)同時(shí)也是mysql程序默認(rèn)使用的語句分隔符,所以在使用mysql程序定義存儲(chǔ)程序時(shí)會(huì)發(fā)生沖突。
解決這個(gè)問題的辦法是使用delimiter命令把mysql程序的語句分隔符重定義為另一個(gè)字符或字符串,它必須是在存儲(chǔ)例程的定義里沒有出現(xiàn)過的。這樣一來,mysql程序就不會(huì)把分號(hào)解釋為語句終止符了,它將把整個(gè)對(duì)象定義作為一條語句傳遞給服務(wù)器。在定義完存儲(chǔ)程序之后,可以把mysql程序的語句終止符重新定義為分號(hào)。
定義一個(gè)存儲(chǔ)過程時(shí)把mysql程序的默認(rèn)分隔符臨時(shí)改變?yōu)?,然后在恢復(fù)了mysql程序的默認(rèn)分隔符之后執(zhí)行了那個(gè)存儲(chǔ)過程:
?
分隔符不必非得是$字符,也不必非得是單個(gè)的字符:
delimiter EOF這里的原則是:只要在某個(gè)存儲(chǔ)程序內(nèi)部的語句里會(huì)用到分號(hào),就應(yīng)該在定義這個(gè)存儲(chǔ)程序時(shí)臨時(shí)改變mysql程序的分隔符。
2.存儲(chǔ)函數(shù)和存儲(chǔ)過程
存儲(chǔ)函數(shù)將向調(diào)用者返回一個(gè)計(jì)算結(jié)果,這個(gè)結(jié)果可以用在表達(dá)式里(就像COS()或HEX()這樣的內(nèi)建函數(shù)那樣)。存儲(chǔ)過程需要使用CALL語句來調(diào)用,是一個(gè)獨(dú)立的操作,不能用在表達(dá)式里。使用存儲(chǔ)過程的情況主要有兩種:(1)只需通過運(yùn)算來實(shí)現(xiàn)某種效果或動(dòng)作而無需返回一個(gè)值,(2)運(yùn)算會(huì)返回多個(gè)結(jié)果集(函數(shù)做不到這一點(diǎn))。這只是些指導(dǎo)性建議,不是硬性規(guī)定。
比如說,如果你需要返回兩個(gè)或更多的值,就不能使用函數(shù)。但你可以使用一個(gè)過程,因?yàn)檫^程支持的參數(shù)類型允許它們的值在過程執(zhí)行期間被設(shè)置,而調(diào)用者可以在過程返回后去訪問那些值。
存儲(chǔ)函數(shù)要用CREATE FUNCTION語句來創(chuàng)建,存儲(chǔ)過程要用CREATE PROCEDURE語句來創(chuàng)建。下面的例子將創(chuàng)建一個(gè)函數(shù),該函數(shù)有一個(gè)代表著年份的整數(shù)參數(shù)。(為了與數(shù)據(jù)表或數(shù)據(jù)列的名字有所區(qū)別,參數(shù)命名時(shí)將使用p_前綴)。
delimiter $ CREATE FUNCTION count_born_in_year(p_year INT) RETURNS INT READS SQL DATA BEGINRETURN (SELECT COUNT(*) FROM president WHERE YEAR(birtb) = p_year); END$ delimiter ;這個(gè)函數(shù)有一條用來表明其返回值數(shù)據(jù)類型的RETURNS子句和一個(gè)用來計(jì)算那個(gè)值的函數(shù)體。函數(shù)體至少需要包含一條RETURN語句,用來向調(diào)用者返回一個(gè)值。把計(jì)算定義為函數(shù)的好處是可以方便地執(zhí)行它而無須每次都寫出所有的邏輯,你可以像使用內(nèi)建函數(shù)那樣來調(diào)用存儲(chǔ)函數(shù):
SELECT count_born_in_year(1990);你無法讓一個(gè)給定的函數(shù)返回多個(gè)值。你可以編寫任意多個(gè)函數(shù),然后在同一條語句里調(diào)用它們?nèi)w。另一個(gè)辦法是使用一個(gè)存儲(chǔ)過程并通過它的OUT參數(shù)“返回”多個(gè)值。存儲(chǔ)過程負(fù)責(zé)計(jì)算那些值并把它們賦值給相應(yīng)的參數(shù),而那些參數(shù)可以在過程返回后由調(diào)用者訪問。如果你定義了一個(gè)與某個(gè)MySQL內(nèi)建函數(shù)同名的存儲(chǔ)函數(shù),在調(diào)用它時(shí)就必須用數(shù)據(jù)庫的名字對(duì)該函數(shù)的名字進(jìn)行限定以避免歧義。
存儲(chǔ)過程和存儲(chǔ)函數(shù)很相似,但它不返回值。因此,它沒有RETURNS子句或任何RETURN語句。下面這個(gè)簡(jiǎn)單的存儲(chǔ)過程和count_born_in_year()函數(shù)很相似,它將顯示一個(gè)結(jié)果集而不是把計(jì)算結(jié)果作為其返回值。
delimiter $ CREATE PROCEDURE show_born_in_year(p_year INT) BEGINSELECT first_name, last_name, birth, deathFROM presidentWHERE YEAR(birth)=P_year; END$ delimiter ;與存儲(chǔ)函數(shù)不同,存儲(chǔ)過程不能用在表達(dá)式里,它們只能通過CALL語句來調(diào)用。如下所示:
CALL show_born_in_year(1990);前面的例子都是選取信息,但存儲(chǔ)例程還可以用來修改數(shù)據(jù)表,如下例所示:
delimiter $ CREATE PROCEDURE update_expiration (p_id INT UNSIGNED, p_date DATE) BEGINUPDATE member SET expiration=p_date WHERE member_id=p_id; END$ delimiter ;存儲(chǔ)函數(shù)必須遵守這樣一條限制:不允許對(duì)調(diào)用本函數(shù)的語句正在讀或?qū)懙臄?shù)據(jù)表進(jìn)行修改。存儲(chǔ)過程通常沒有這個(gè)限制,但如果它們是從存儲(chǔ)函數(shù)里被調(diào)用,就需要遵守這條限制。
3.存儲(chǔ)函數(shù)和存儲(chǔ)過程的權(quán)限
存儲(chǔ)函數(shù)和存儲(chǔ)過程屬于數(shù)據(jù)庫。要想創(chuàng)建存儲(chǔ)函數(shù)或存儲(chǔ)過程,必須擁有那個(gè)數(shù)據(jù)庫的CREATE ROUTINE權(quán)限。在默認(rèn)的情況下,當(dāng)你創(chuàng)建一個(gè)存儲(chǔ)例程時(shí),服務(wù)器將自動(dòng)地把EXECUTE和ALTER ROUTINE權(quán)限授予你(如果你還沒有獲得這些權(quán)限),這樣你才可以執(zhí)行那個(gè)例程或刪除它。當(dāng)你刪除那個(gè)例程時(shí),服務(wù)器將自動(dòng)撤銷那些權(quán)限。如果你不想使用這種自動(dòng)化的權(quán)限授予/撤銷機(jī)制,把a(bǔ)utomatic_sp_privileges系統(tǒng)變量設(shè)置為0即可。
如果服務(wù)器啟用了二進(jìn)制日志功能,存儲(chǔ)函數(shù)還需要遵守一些額外的限制條件(不允許創(chuàng)建不確定或是會(huì)修改數(shù)據(jù)的存儲(chǔ)函數(shù))以保證二進(jìn)制日志能夠安全地完成備份和復(fù)制操作。這些限制條件如下:
1.如果log_bin_trust_function_creators系統(tǒng)變量沒有被激活,你就必須具備SUPER權(quán)限才能創(chuàng)建存儲(chǔ)函數(shù)。在此前提下,你創(chuàng)建的每一個(gè)函數(shù)都必須是確定的,并且不得修改數(shù)據(jù)。為了表明這一點(diǎn),需要使用DETERMINISTIC、NO SQL或READS SQL DATA之一來定義存儲(chǔ)函數(shù)。
2.如果log bin_trust_function_creators系統(tǒng)變量已被激活,則沒有任何限制。只有當(dāng)你可以相信MySQL服務(wù)器上的所有用戶都不會(huì)去定義不安全的存儲(chǔ)函數(shù)時(shí),這種設(shè)置才是最適當(dāng)?shù)摹?/p>
與log_bin_trust_function_creators系統(tǒng)變量有關(guān)的限制條件同樣適用于觸發(fā)器的創(chuàng)建工作。
4.存儲(chǔ)過程的參數(shù)類型
存儲(chǔ)過程的參數(shù)分為3種類型。對(duì)于IN參數(shù),調(diào)用者把一個(gè)值傳遞給過程,過程可以對(duì)這個(gè)值進(jìn)行修改,但任何修改在過程返回后對(duì)調(diào)用者是不可見的。OUT參數(shù)剛好相反,過程把一個(gè)值賦值給OUT參數(shù),這個(gè)值在過程返回后可以由調(diào)用者訪問。INOUT參數(shù)允許調(diào)用者向過程傳遞一個(gè)值,然后再取回一個(gè)值。
要想明確地為參數(shù)指定類型,在參數(shù)表里把IN, OUT或INOUT寫在參數(shù)名字前面即可。如果沒有為參數(shù)指定類型,其默認(rèn)類型將是IN。
在使用OUT或INOUT參數(shù)時(shí),在調(diào)用過程時(shí)需要給出一個(gè)變量名。過程可以設(shè)置參數(shù)的值,相應(yīng)的變量將在過程返回時(shí)獲得那個(gè)值。如果想讓某個(gè)存儲(chǔ)過程返回多個(gè)結(jié)果值,OUT和INOUT參數(shù)類型將非常有用(存儲(chǔ)函數(shù)只能返回一個(gè)值,不能勝任)。下面的過程演示了OUT參數(shù)的用法。它將分別統(tǒng)計(jì)出student數(shù)據(jù)表里的男生和女生人數(shù)并通過它的參數(shù)返回這兩個(gè)計(jì)數(shù)值,讓調(diào)用者可以訪問它們:
delimiter $ CREATE PROCEDURE count_students_by_sex(OUT p_male INT, OUT p_female INT) BEGINSELECT COUNT(*) FROM student WHERE sex= 'F' INTO p_female;SELECT COUNT(*) FROM student WHERE sex= 'F' INTO p_female; END$ delimiter ;在調(diào)用這個(gè)過程時(shí),請(qǐng)把各個(gè)參數(shù)替換為相應(yīng)的用戶定義變量。這個(gè)過程將把計(jì)數(shù)值放到這些參數(shù)里,在它返回之后,那些變量將包含計(jì)數(shù)值:
CALL count_students_by_sex(@mcoant, @fcount); SELECT 'Number of male students:',@mcount;IN、OUT和INOUT關(guān)鍵字不適用于存儲(chǔ)函數(shù)、觸發(fā)器或事件。對(duì)于存儲(chǔ)函數(shù),所有的參數(shù)都像IN參數(shù)。觸發(fā)器和事件則根本沒有任何參數(shù)。下一部分介紹:觸發(fā)器和事件。
轉(zhuǎn)載于:https://www.cnblogs.com/houkai/p/3523563.html
總結(jié)
以上是生活随笔為你收集整理的存储程序(1)——MYSQL的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 太氪金:《暗黑:不朽》国服上线遭玩家疯狂
- 下一篇: 数据库之间数据转换最快方法