存儲過程:一組預編譯的SQL語句和流程控制語句,被命名并存儲在數據庫中。存儲過程可以用來封裝復雜的數據庫操作邏輯,并在需要時進行調用。
類似的操作還有:自定義函數、.sql文件導入。
我們先從熟悉的函數開始說起:
自定義函數
User-Defined Functions(UDF)允許創建 可以在SQL查詢中調用的函數,以便在執行特定操作或邏輯。
創建函數:
create function 函數名(參數名 數據類型,...) -- 參數列表
returns 返回值類型
[deterministic]
begin-- 函數體return 返回值;
end;
關鍵字:deterministic 漢譯為 ‘確定的’ 。在MySQL8.0版本以上,創建函數必須在該位置添加關鍵字,否則會報錯:
如果加上關鍵字deterministic的話:
另外需要注意的是:在指明函數返回值類型時,使用的關鍵字是?returns,在函數體內部返回值時使用的是?return?。注意區別,不要寫錯。
刪除函數:
將自定義函數刪除,語法:
drop function [if exists] myfuc;
調用函數:
使用自定義函數的方法與使用內置函數的方式一樣,執行select語句:
select myfuc();
變量
變量的聲明:關鍵字declare
declare variable datatype [default val];-- eg.
declare age int default 18;
變量的賦值:關鍵字set
set variable = val;-- eg.
set age = age + 10;
注意事項:
第一、函數的參數是已經聲明好的變量,無需再次聲明,可以直接使用
第二、聲明變量的語句必須在函數體的最上方,其次才可以是其它語句
第三、函數體外的SQL語句也可以設置變量,語法為:【set @variable = val】
在函數體內執行sql語句為變量賦值:
-- function_head
begindeclare val int default 0;select count(*) into val from emp;return val;end;
在該示例中,就將查詢的結果直接賦給變量val。?
結構語句
分支結構
SQL語句中一共有兩種分支結構【if/case】,但這兩種分支結構的語法用法卻與我們熟知的語言的寫法不同,但很好理解。下面我們就具體看一下:
第一種分支語句:if then...else if then...else...end if;
if condition1 then-- coding1
else if condition2 then-- coding2
else if condition3 then-- coding3
else--codingn
end if;
與我們常見的C/C++的代碼作用域以花括號作為界符不同,SQL語言使用 end顯式指定該作用域結束。上面定義函數時begin......end;就已經體現了。在if里面作為begin的等價關鍵字為:then。編譯器一旦識別then就知道進入if的內部作用域了。再次識別到其它的else if或者end if,就會跳出本作用域進入下一個作用域。
第二種分支語句:case? ?when then...when then... else... end case;
casewhen condition1 then-- exp1...;when condition2 then-- exp2...;else-- expn...;
end case;
case語句與我們熟悉的語法也不一樣,不過也很好理解:遇到case直接進入分支,然后判斷條件,滿足即then執行作用域內的邏輯代碼或表達式。最后有一個else相當于C/C++中的default,都不滿足就給個默認入口。最后以end case結束分支作用域。
循環結構
同樣的,循環結構也有兩種語法:【while/repeat】。
第一種循環語句:while (bool)?do ... end while;
while _condition_
do---- coding --
end while;
當_condition_條件滿足時 do執行循環體,直到條件不滿足,end while結束循環。
第二種循環語句:repeat ... until (bool) end repeat;
repeat---- coding--
until _condition_
end repeat;
與while循環不同,while循環時滿足條件才執行。這種循環語句是,當滿足了_condtion_條件時,會結束這個循環。
存儲過程
存儲過程類似于自定義函數的語法,但是有區別的。例如:無返回值、傳入的參數方式不同、可以使用游標等。
基礎語法
無參語法:
-- 創建存儲過程
create procedure 存儲過程名()
begin-- 存儲過程的邏輯代碼-- 可以包含SQL語句、控制結構和變量操作
end;
調用存儲過程:
-- 執行存儲過程
call procedure 存儲過程名();
刪除存儲過程:
-- 刪除存儲過程
drop procedure [if exists] 存儲過程名;
參數詳解
create procedure 存儲過程名([in|out|inout] 參數名 參數的數據類型,[in|out|inout] 參數名 參數的數據類型,......
)
begin-- 存儲過程的邏輯代碼-- sql語句、結構語句、變量操作等
end;
類型修飾符
存儲過程的每個參數都有輸入輸出修飾,默認為:in輸入參數
in(默認):輸入參數,存儲過程的輸入值,從外部傳遞給存儲過程,存儲過程內部是只讀的,不能修改它的值。【readonly】
out:輸出參數,存儲過程的返回值,存儲過程可以修改它的值并將其返回
inout:輸入和輸出參數,既可以作為輸入值傳遞給存儲過程,也可以由存儲過程修改并返回。
進階語法
我們現在有一個需求,需要查詢emp表,為每個員工加100元薪資。
現在遇到的難點是:emp表查詢到的結果不是單一結果,只知道sal的字段類型為int,那么如何將結果集的每一條記錄依次取出來呢?
我們需要學習一個新的知識:游標-cursor
!!!注意!!!
在MySQL中,存儲過程允許使用游標來處理結果集,但函數里不行。可能是因為函數的設計用途不同。函數通常被要求是確定性的,或者至少在某些上下文中不允許有副作用,而游標可能涉及到對結果集的操作,可能引起非確定性的結果或者副作用。或者,函數的執行環境限制了一些操作,比如不允許修改數據庫狀態,而游標可能用于逐行處理,但函數需要返回單個值,這樣的結構不支持
游標
在MySQL中,游標Cursor是一種數據庫對象,它能夠讓你對結果集逐行進行處理。在操作數據庫時,普通的SQL語句通常會對整個及進行操作,但在我們上述的場景下,就需要逐行的處理數據,這時有標記就能發揮作用了。
游標的使用步驟:聲明=》打開=》讀取=》關閉=》釋放
-- 聲明游標
-- 定義游標的名稱 并綁定查詢語句
declare cs cursor
for
select empno from emp;
游標就類似于C++實例化出來的對象:cs為對象名,對象類型為cursor-游標,綁定查詢語句就是傳入構造參數初始化游標的。
-- 打開游標
-- 執行定義好的查詢語句 把結果集存在游標中
open cs;
聯系熟悉的C++幫助理解:open 就是對象的一個成員函數,作用是執行綁定的sql語句獲取結果集的。
-- 讀取數據
-- 借助fetch語句從游標逐行獲取數據
fetch cs into val;
fetch 就是游標的另一個成員函數:換個名字理解:getNextData-獲取下一條數據,內部有一個偏移量,再次執行時,就是取結果集的下一個了。跟文件操作的偏移量聯系一下也不難理解。
-- 關閉游標
-- 結束對結果集的操作后,關閉游標以釋放資源
close cs;
?調用內部成員函數close,相當于調用clear函數
最后還有一個釋放游標,也就是從內存中移除游標的階段。
注意事項:
- 性能問題:游標會對數據庫性能產生影響,因為它是逐行處理數據的,所以在大數據集上使用時要謹慎。
- 資源管理:使用完游標后,一定要關閉并釋放它,避免資源浪費。
- 異常處理:要考慮游標操作中可能出現的異常,像結果集為空或者到達結果集末尾等情況。
異常處理:錯誤處理-句柄
上面我們也說了,游標在使用時可能會出現異常情況。例如:循環次數過多,但數據集項數少于循環次數,那么會產生異常情況。下面給出一段代碼:
create procedure mypro()
begin declare i int default 0;declare eid int;declare cs cursorforselect empno from emp;open cs;while i<20 dofetch cs into eid;select sal from emp where empno=eid;set i = i+1;end while;close cs;
end;
已知,我們的emp表一共只有14條記錄,那怎么從cs中取二十次數據呢?肯定會出錯的,這個異常一旦出現,我們如何處理呢?SQL就提供了一種異常處理機制:句柄
句柄-處理類型:
????????continue - 繼續執行后續代碼 - 用于可恢復的錯誤(數據遍歷結束)
????????exit - 退出當前代碼塊 - 用于不可恢復的錯誤(如事務沖突)
錯誤-條件類型:
????????not found - 未找到
????????sqlexception - sql異常
????????sqlwarning - sql警報
????????特定錯誤碼 -如1062主鍵沖突
根據笛卡爾積的形式,從兩個類型集合中任取一個都可以組成一個異常處理類型。
-- 聲明處理句柄declare continue handler for not found set done=1;declare exit handler for sqlexceptionbeginrollback; -- 回滾事務set errmsg="錯誤信息";end;declare continue handler for sqlwarning set done=2;
感謝大家!