MySQL存儲過程
一、存儲過程
1.1 什么是存儲過程
存儲過程(Stored Procedure)是在大型數據庫系統中,一組為了完成特定功能的SQL 語句集,它存儲在數據庫中,一次編譯后永久有效,用戶通過指定存儲過程的名字并給出參數(如果該存儲過程帶有參數)來執行它。存儲過程是數據庫中的一個重要對象。在數據量特別龐大的情況下利用存儲過程能達到倍速的效率提升
1.2 數據庫存儲過程程序
當我們了了解存儲過程是什么之后,就需要了解數據庫中存在的這三種類型的數據庫存儲類型程序,如下:
存儲過程:?存儲過程是最常見的存儲程序,存儲過程是能夠接受輸入和輸出參數并且能夠在請求時被執行的程序單元。
存儲函數:?存儲函數和存儲過程很相像,但是它的執行結果會返回一個值。最重要的是存儲函數可以被用來充當標準的 SQL 語句,允許程序員有效的擴展 SQL 語言的能力。
觸發器:?觸發器是用來響應激活或者觸發數據庫行為事件的存儲程序。通常,觸發器用來作為數據庫操作語言的響應而被調用,觸發器可以被用來作為數據校驗和自動反向格式化。
注意: 其他的數據庫提供了別的數據存儲程序,包括包和類。目前MySQL不提供這種結構。
1.3 為什么要使用存儲程序
雖然目前的開發中存儲程序我們使用的并不是很多,但是不一定就否認它。其實存儲程序會為我們使用和管理數據庫帶來了很多優勢:
使用存儲程序更加安全。
存儲程序提供了一種數據訪問的抽象機制,它能夠極大的改善你的代碼在底層數據結構演化過程中的易維護性。
存儲程序可以降低網絡擁阻,因為屬于數據庫服務器的內部數據,這相比在網上傳輸數據要快的多。
存儲程序可以替多種使用不同構架的外圍應用實現共享的訪問例程,無論這些構架是基于數據庫服務器外部還是內部。
以數據為中心的邏輯可以被獨立的放置于存儲程序中,這樣可以為程序員帶來更高、更為獨特的數據庫編程體驗。
在某些情況下,使用存儲程序可以改善應用程序的可移植性。(在另外某些情況下,可移植性也會很差!)
這里我大致解釋一下上述幾種使用存儲程序的優勢:
我們要知道在Java語言中,我們使用數據庫與Java代碼結合持久化存儲需要引入JDBC來完成。會想到JDBC,我們是否還能想起SQL注入問題呢?雖然使用PreparedStatement解決SQL注入問題,那就真的是絕對安全嗎?不,它不是絕對安全的。
這時候分析一下數據庫與Java代碼的連接操作流程。在BS結構中,一般都是瀏覽器訪問服務器的,再由服務器發送SQL語句到數據庫,在數據庫中對SQL語句進行編譯運行,最后把結果通過服務器處理再返回瀏覽器。在此操作過程中,瀏覽器對服務器每發送一次對數據庫操作的請求就會調用對應的SQL語句編譯和執行,這是一件十分浪費性能的事情,?性能下降了就說明對數據庫的操作?效率低?了。
還有一種可能是,在這個過程中進行發送傳輸的SQL語句是對真實的庫表進行操作的SQL語句,如果在發送傳輸的過程中被攔截了,一些不法分子會根據他所攔截的SQL語句推斷出我們數據庫中的庫表結構,這是一個很大的?安全隱患?。
關于可維護性的提高,這里模擬一個場景。通常數據庫在公司中是由DBA來管理的,如果管理數據庫多年的DBA辭職了,此時數據庫會被下一任DBA來管理。這里時候問題來了,數據庫中這么多的數據和SQL語句顯然對下一任管理者不太友好。就算管理多年的DBA長時間不操作查看數據庫也會忘記點什么東西。所以,我們在需要引入存儲程序來進行SQL語句的統一編寫和編譯,?為維護提供了便利?。(其實我覺得這個例子并不生動合理,但是為了大家能理解,請體諒!)
講了很多存儲程序的優勢演變過程,其核心就是:需要將編譯好的一段或多段SQL語句放置在數據庫端的存儲程序中,以便解決以上問題并方便開發者直接調用。
二、存儲過程的使用步驟
2.1 存儲過程的開發思想
存儲過程時數據庫的一個重要的對象,可以封裝SQL語句集,可以用來完成一些較復雜的業務邏輯,并且可以入參(傳參)、出參(返回參數),這里與Java中封裝方式十分相似。
而且創建時會預先編譯后保存,開發者后續的調用都不需要再次編譯。
2.2 存儲過程的優缺點
存儲過程使用的優缺點其實在1.3中的優勢中說到了。這里我簡單羅列一下存儲過程的優點與缺點。
優點:
在生產環境下,可以通過直接修改存儲過程的方式修改業務邏輯或bug,而不用重啟服務器。
執行速度快,存儲過程經過編譯之后會比單獨一條一條編譯執行要快很多。
減少網絡傳輸流量。
便于開發者或DBA使用和維護。
在相同數據庫語法的情況下,改善了可移植性。
缺點:
過程化編程,復雜業務處理的維護成本高。
調試不便。
因為不同數據庫語法不一致,不同數據庫之間可移植性差。
2.3 MySQL存儲過程的官方文檔
英語好或者有能力的小伙伴可以去參考一下官方文檔。如果不參考官方文檔,沒關系,我在下面也會詳細講述MySQL存儲過程的各個知識點。
1https://dev.mysql.com/doc/refman/5.6/en/preface.html
2.3 存儲過程的使用語法
1createPROCEDURE 過程名( in|out|inout 參數名 數據類型 , ...)2begin3sql語句;4end;5call過程名(參數值);
in 是定義傳入參數的關鍵字。 out 是定義出參的關鍵字。 inout 是定義一個出入參數都可以的參數。如果括號內什么都不定義,就說明該存儲過程時一個無參的函數。在后面會有詳細的案例分析。
注意:SQL語句默認的結束符為 ; ,所以在使用以上存儲過程時,會報1064的語法錯誤。我們可以使用 DELIMITER 關鍵字臨時聲明修改SQL語句的結束符為 // ,如下:
1-- 臨時定義結束符為"http://"2DELIMITER//3create PROCEDURE 過程名(in|out參數名 數據類型 , ...)4begin5sql語句;6end//7-- 將結束符重新定義回結束符為";"8DELIMITER ;
例如:使用存儲過程來查詢員工的工資(無參)
注意:如果在特殊的必要情況下,我們還可以通過 delimiter 關鍵字將 ; 結束符聲明回來使用,在以下案例中我并沒有這樣將結束符聲明回原來的 ; ,在此請大家注意~
為什么我在這里提供了drop(刪除)呢?
是因為我們在使用的時候如果需要修改存儲過程中的內容,我們需要先刪除現有的存儲過程后,再creat重新創建。
1# 聲明結束符為//2delimiter//34# 創建存儲過程(函數)5createprocedurese()6begin7selectsalaryfromemployee;8end//910# 調用函數11callse()//1213# 刪除已存在存儲過程——se()函數14dropprocedureifexistsse//
三、存儲過程的變量和賦值
3.1 局部變量
聲明局部變量語法: declare var_name type [default var_value];
賦值語法:
注意:局部變量的定義,在begin/end塊中有效。
使用set為參數賦值
1# set賦值23# 聲明結束符為//4delimiter//56# 創建存儲過程7createprocedureval_set()8begin9# 聲明一個默認值為unknown的val_name局部變量10declareval_namevarchar(32)default'unknown';11# 為局部變量賦值12setval_name='Centi';13# 查詢局部變量14selectval_name;15end//1617# 調用函數18callval_set()//19
使用into接收參數
1delimiter // 2create procedure val_into() 3begin 4# 定義兩個變量存放name和age5declareval_namevarchar(32)default'unknown'; 6declareval_ageint; 7# 查詢表中id為1的name和age并放在定義的兩個變量中8selectname,ageintoval_name,val_agefromemployeewhereid=1; 9# 查詢兩個變量10selectval_name,val_age;11end //1213call val_into() //14
3.2 用戶變量
用戶自定義用戶變量,當前會話(連接)有效。與Java中的成員變量相似。
語法:?@val_name
注意:?該用戶變量不需要提前聲明,使用即為聲明。
1delimiter//2createprocedureval_user()3begin4# 為用戶變量賦值5set@val_name='Lacy';6end//78# 調用函數9callval_user()//1011# 查詢該用戶變量12select@val_name//
3.3 會話變量
會話變量是由系統提供的,只在當前會話(連接)中有效。
語法: @@session.val_name
1# 查看所有會話變量2showsessionvariables;3# 查看指定的會話變量4select@@session.val_name;5# 修改指定的會話變量6set@@session.val_name=0;
這里我獲取了一下所有的會話變量,大概有500條會話變量的記錄。等我們深入學習MySQL后,了解了各個會話變量值的作用,可以根據需求和場景來修改會話變量值。
1delimiter//2createprocedure val_session()3begin4# 查看會話變量5show session variables;6end//78callval_session() //9
image-20200610112512964
3.4 全局變量
全局變量由系統提供,整個MySQL服務器內有效。
語法: @@global.val_name
1# 查看全局變量中變量名有char的記錄2showglobalvariables like'%char%'//3# 查看全局變量character_set_client的值4select@@global.character_set_client//
3.5 入參出參
入參出參的語法我們在文章開頭已經提過了,但是沒有演示,在這里我將演示一下入參出參的使用。
語法: in|out|inout 參數名 數據類型 , ...
in 定義出參; out 定義入參; inout 定義出參和入參。
出參in
使用出參in時,就是需要我們傳入參數,在這里可以對參入的參數加以改變。簡單來說in只負責傳入參數到存儲過程中,類似Java中的形參。
1delimiter//2createprocedureval_in(inval_namevarchar(32))3begin4# 使用用戶變量出參(為用戶變量賦參數值)5set@val_name1=val_name;6end//78# 調用函數9callval_in('DK')//1011# 查詢該用戶變量12select@val_name1//
入參out
在使用out時,需要傳入一個參數。而這個參數相當于是返回值,可以通過調用、接收來獲取這個參數的內容。簡單來說out只負責作返回值。
1delimiter//2# 創建一個入參和出參的存儲過程3create procedureval_out(inval_idint,outval_name varchar(32)) 4begin 5? ? # 傳入參數val_id查詢員工返回name值(查詢出的name值用出參接收并返回) 6selectnameintoval_namefromemployeewhereid= val_id;7end//89# 調用函數傳入參數并聲明傳入一個用戶變量10callval_out(1, @n)//1112# 查詢用戶變量13select @n//
入參出參inout
inout關鍵字,就是把in和out合并成了一個關鍵字使用。被關鍵字修飾的參數既可以出參也可以入參。
1delimiter // 2create procedure val_inout(in val_name varchar(32), inout val_age int) 3begin 4# 聲明一個a變量5declareaint; 6# 將傳入的參數賦值給a變量7seta = val_age; 8# 通過name查詢age并返回val_age9selectageintoval_agefromemployeewherename= val_name;10# 將傳入的a與-和查詢age結果字符串做拼接并查詢出來(concat——拼接字符串)11selectconcat(a,'-', val_age);12end //1314# 聲明一個用戶變量并賦予參數為4015set @ages = '40' //16# 調用函數并傳入參數值17call val_inout('Ziph', @ages) //18# 執行結果19# 40-18
四、存儲過程中的流程控制
4.1 if 條件判斷(推薦)
擴展: timestampdiff(unit, exp1, exp2) 為exp2 - exp1得到的差值,而單位是unit。(常用于日期)
擴展例子: select timestampdiff(year,’2020-6-6‘,now()) from emp e where id = 1;
解釋擴展例子:查詢員工表中id為1員工的年齡,exp2就可以為該員工的出生年月日,并以年為單位計算。
語法:
1IF條件判斷 THEN 結果2[ELSEIF 條件判斷 THEN 結果] ...3[ELSE 結果]4ENDIF
舉例:傳入所查詢的id參數查詢工資標準(s<=6000為低工資標準;6000 <=10000為中工資標準;10000 <=15000為中上工資標準;s style="font-size: inherit; color: inherit; line-height: inherit; margin: 0px; padding: 0px;">=15000為高工資標準)
1delimiter//2createprocedures_sql(inval_idint)3begin4# 聲明一個局部變量result存放工資標準結果5declareresultvarchar(32);6# 聲明一個局部變量存放查詢得到的工資7declaresdouble;8# 根據入參id查詢工資9selectsalaryintosfromemployeewhereid=val_id;10# if判斷的使用11ifs<=6000then12setresult='低工資標準';13elseifs<=10000then14setresult='中工資標準';15elseifs<=15000then16setresult='中上工資標準';17else18setresult='高工資標準';19endif;20# 查詢工資標準結果21selectresult;22end//2324# 調用函數,傳入參數25calls_sql(1);
4.2 case條件判斷
關于case語句,不僅僅在存儲過程中可以使用,MySQL基礎查詢語句中也有用到過。相當于是Java中的switch語句。
語法:
1# 語法一2CASEcase_value3WHENwhen_valueTHEN結果4[WHENwhen_valueTHEN結果]...5[ELSE結果]6ENDCASE78# 語法二(推薦語法)9CASE10WHEN條件判斷THEN結果11[WHEN條件判斷THEN結果]...12[ELSE結果]13ENDCASE
舉例:
1# 語法一2delimiter//3create procedures_case(inval_idint) 4begin 5? ? # 聲明一個局部變量result存放工資標準結果 6? ? declare resultvarchar(32);7# 聲明一個局部變量存放查詢得到的工資8declare sdouble;9# 根據入參id查詢工資10selectsalaryintosfromemployeewhereid = val_id;11cases12when6000thensetresult ='低工資標準';13when10000thensetresult ='中工資標準';14when15000thensetresult ='中上工資標準';15elsesetresult ='高工資標準';16endcase;17selectresult;18end//1920calls_case(1);2122# 語法二(推薦)23delimiter//24create procedures_case(inval_idint)25begin26? ? # 聲明一個局部變量result存放工資標準結果27? ? declare resultvarchar(32);28# 聲明一個局部變量存放查詢得到的工資29declare sdouble;30# 根據入參id查詢工資31selectsalaryintosfromemployeewhereid = val_id;32case33whens <=6000thensetresult ='低工資標準';34whens <=10000thensetresult ='中工資標準';35whens <=15000thensetresult ='中上工資標準';36elsesetresult ='高工資標準';37endcase;38selectresult;39end//4041calls_case(1);
4.3 loop循環
loop為死循環,需要手動退出循環,我們可以使用 leave 來退出循環
可以把leave看成Java中的break;與之對應的,就有 iterate (繼續循環)也可以看成Java的continue
語法:
1[別名:]LOOP2? ? 循環語句3ENDLOOP[別名]
注意:別名和別名控制的是同一個標簽。
示例1:循環打印1~10(leave控制循環的退出)
注意:該loop循環為死循環,我們查的1~10數字是i,在死循環中設置了當大于等于10時停止循環,也就是說先后執行了10次該循環內的內容,結果查詢了10次,生成了10個結果(1~10)。
1delimiter//2createprocedures_loop()3begin4# 聲明計數器5declareiintdefault1;6# 開始循環7? ? num:8loop9# 查詢計數器記錄的值10selecti;11# 判斷大于等于停止計數12ifi>=10then13leavenum;14endif;15# 計數器自增116seti=i+1;17# 結束循環18endloopnum;19end//2021calls_loop();
打印結果:
image-20200610191639524
示例2:循環打印1~10(iterate和leave控制循環)
注意:這里我們使用字符串拼接計數器結果,而條件如果用iterate就必須時 i < 10 了!
1delimiter//2createprocedures_loop1()3begin4# 聲明變量i計數器5declareiintdefault1;6# 聲明字符串容器7declarestrvarchar(256)default'1';8# 開始循環9? ? num:10loop11# 計數器自增112seti=i+1;13# 字符串容器拼接計數器結果14setstr=concat(str,'-',i);15# 計數器i如果小于10就繼續執行16ifi<10then17iteratenum;18endif;19# 計數器i如果大于10就停止循環20leavenum;21# 停止循環22endloopnum;23# 查詢字符串容器的拼接結果24selectstr;25end//2627calls_loop1();
image-20200610193153512
4.4 repeat循環
repeat循環類似Java中的do while循環,直到條件不滿足才會結束循環。
語法:
1[別名:]REPEAT2? ? 循環語句3UNTIL 條件4END REPEAT[別名]
示例:循環打印1~10
1delimiter//2createprocedures_repeat()3begin4declareiintdefault1;5declarestrvarchar(256)default'1';6# 開始repeat循環7? ? num:8repeat9seti=i+1;10setstr=concat(str,'-',i);11# until 結束條件12# end repeat 結束num 結束repeat循環13untili>=10endrepeatnum;14# 查詢字符串拼接結果15selectstr;16end//1718calls_repeat();
4.5 while循環
while循環就與Java中的while循環很相似了。
語法:
1[別名]WHILE條件DO2循環語句3ENDWHILE[別名]
示例:循環打印1~10
1delimiter//2createprocedures_while()3begin4declareiintdefault1;5declarestrvarchar(256)default'1';6# 開始while循環7? ? num:8# 指定while循環結束條件9whilei<10do10seti=i+1;11setstr=concat(str,'+',i);12# while循環結束13endwhilenum;14# 查詢while循環拼接字符串15selectstr;16end//1718calls_while();
4.6 流程控制語句(繼續、結束)
至于流程控制的繼續和結束,我們在前面已經使用過了。這里再列舉一下。
leave:與Java中break;相似
1leave標簽;
iterate:與Java中的continue;相似
1iterate 標簽;
五、游標與handler
5.1 游標
游標是可以得到某一個結果集并逐行處理數據。游標的逐行操作,導致了游標很少被使用!
語法:
1DECLARE游標名 CURSOR FOR 查詢語句2--打開語法3OPEN游標名4--取值語法5FETCH游標名 INTO var_name [, var_name] ...6--關閉語法7CLOSE游標名
了解了游標的語法,我們開始使用游標。如下:
示例:使用游標查詢id、name和salary。
1delimiter//2createproceduref()3begin4declareval_idint;5declareval_namevarchar(32);6declareval_salarydouble;78# 聲明游標9declareemp_flagcursorfor10selectid,name,salaryfromemployee;1112# 打開13openemp_flag;1415# 取值16fetchemp_flagintoval_id,val_name,val_salary;1718# 關閉19closeemp_flag;2021selectval_id,val_name,val_salary;22end//2324callf();
執行結果:
image-20200610203622749
因為游標逐行操作的特點,導致我們只能使用游標來查詢一行記錄。怎么改善代碼才可以實現查詢所有記錄呢?聰明的小伙伴想到了使用循環。對,我們試試使用一下循環。
1delimiter//2createproceduref()3begin4declareval_idint;5declareval_namevarchar(32);6declareval_salarydouble;78# 聲明游標9declareemp_flagcursorfor10selectid,name,salaryfromemployee;1112# 打開13openemp_flag;1415# 使用循環取值16c:loop17# 取值18fetchemp_flagintoval_id,val_name,val_salary;19endloop;2021# 關閉22closeemp_flag;2324selectval_id,val_name,val_salary;25end//2627callf();
image-20200610204034224
我們使用循環之后,發現有一個問題,因為循環是死循環,我們不加結束循環的條件,游標會一直查詢記錄,當查到沒有的記錄的時候,就會拋出異常 1329:未獲取到選擇處理的行數 。
如果我們想辦法指定結束循環的條件該怎么做呢?
這時候可以聲明一個boolean類型的標記。如果為true時則查詢結果集,為false時則結束循環。
1delimiter//2createproceduref()3begin4declareval_idint;5declareval_namevarchar(32);6declareval_salarydouble;78# 聲明flag標記9declareflagbooleandefaulttrue;1011# 聲明游標12declareemp_flagcursorfor13selectid,name,salaryfromemployee;1415# 打開16openemp_flag;1718# 使用循環取值19c:loop20fetchemp_flagintoval_id,val_name,val_salary;21# 如果標記為true則查詢結果集22ifflagthen23selectval_id,val_name,val_salary;24# 如果標記為false則證明結果集查詢完畢,停止死循環25else26leavec;27endif;28endloop;2930# 關閉31closeemp_flag;3233selectval_id,val_name,val_salary;34end//3536callf();
上述代碼你會發現并沒有寫完,它留下了一個很嚴肅的問題。當flag = false時候可以結束循環。但是什么時候才讓flag為false啊?
于是,MySQL為我們提供了一個 handler 句柄。它可以幫我們解決此疑惑。
handler句柄語法: declare continue handler for 異常 set flag = false;
handler句柄可以用來捕獲異常,也就是說在這個場景中當捕獲到 1329:未獲取到選擇處理的行數 時,就將flag標記的值改為false。這樣使用handler句柄就解決了結束循環的難題。讓我們來試試吧!
終極版示例:解決了多行查詢以及結束循環問題。
1delimiter//2createproceduref()3begin4declareval_idint;5declareval_namevarchar(32);6declareval_salarydouble;78# 聲明flag標記9declareflagbooleandefaulttrue;1011# 聲明游標12declareemp_flagcursorfor13selectid,name,salaryfromemployee;1415# 使用handler句柄來解決結束循環問題16declarecontinuehandlerfor1329setflag=false;1718# 打開19openemp_flag;2021# 使用循環取值22c:loop23fetchemp_flagintoval_id,val_name,val_salary;24# 如果標記為true則查詢結果集25ifflagthen26selectval_id,val_name,val_salary;27# 如果標記為false則證明結果集查詢完畢,停止死循環28else29leavec;30endif;31endloop;3233# 關閉34closeemp_flag;3536selectval_id,val_name,val_salary;37end//3839callf();
執行結果:
image-20200610210925964
在執行結果中,可以看出查詢結果以多次查詢的形式,分布顯示到了每一個查詢結果窗口中。
注意:在語法中,變量聲明、游標聲明、handler聲明是必須按照先后順序書寫的,否則創建存儲過程出錯。
5.2 handler句柄
語法:
1DECLAREhandler操作 HANDLER2FOR 情況列表...(比如:異常錯誤情況)3操作語句
注意:異常情況可以寫異常錯誤碼、異常別名或SQLSTATE碼。
handler操作:
CONTINUE:?繼續
EXIT:?退出
UNDO:?撤銷
異常情況列表:
mysql_error_code
SQLSTATE [VALUE] sqlstate_value
condition_name
SQLWARNING
NOT FOUND
SQLEXCEPTION
注意:MySQL中各種異常情況代碼、錯誤碼、別名和SQLSTATEM碼可參考官方文檔:
https://dev.mysql.com/doc/refman/5.6/en/server-error-reference.html
寫法示例:
1DECLAREexitHANDLERFORSQLSTATE'3D000'setflag =false;2DECLAREcontinueHANDLERFOR1050setflag =false;3DECLAREcontinueHANDLERFORnotfoundsetflag =false;
六、循環創建表
需求:創建下個月的每天對應的表,創建的表格式為: comp_2020_06_01、comp_2020_06_02、...
描述:我們需要用某個表記錄很多數據,比如記錄某某用戶的搜索、購買行為(注意,此處是假設用數據庫保存),當每天記錄較多時,如果把所有數據都記錄到一張表中太龐大,需要分表,我們的要求是,每天一張表,存當天的統計數據,就要求提前生產這些表——每月月底創建下一個月每天的表!
預編譯: PREPARE 數據庫對象名 FROM 參數名
執行: EXECUTE 數據庫對象名 [USING @var_name [, @var_name] ...]
通過數據庫對象創建或刪除表: {DEALLOCATE | DROP} PREPARE 數據庫對象名
關于時間處理的語句:
1--EXTRACT(unit FROM date)? ? ? ? ? ? ? 截取時間的指定位置值2--DATE_ADD(date,INTERVAL expr unit)? ? 日期運算3--LAST_DAY(date)? ? ? ? ? ? ? ? ? ? ? ? ? 獲取日期的最后一天4--YEAR(date)? ? ? ? ? ? ? ? ? ? ? ? ? ? 返回日期中的年5--MONTH(date)? ? ? ? ? ? ? ? ? ? ? ? ? ? 返回日期的月6--DAYOFMONTH(date)? ? ? ? ? ? ? ? ? ? ? ? 返回日
代碼:
1--思路:循環構建表名comp_2020_06_01到comp_2020_06_30;并執行create語句。2delimiter//3createproceduresp_create_table()4begin5# 聲明需要拼接表名的下一個月的年、月、日6declarenext_yearint;7declarenext_monthint;8declarenext_month_dayint;910# 聲明下一個月的月和日的字符串11declarenext_month_strchar(2);12declarenext_month_day_strchar(2);1314# 聲明需要處理每天的表名15declaretable_name_strchar(10);1617# 聲明需要拼接的118declaret_indexintdefault1;19# declare create_table_sql varchar(200);2021# 獲取下個月的年份22setnext_year=year(date_add(now(),INTERVAL1month));23# 獲取下個月是幾月 24setnext_month=month(date_add(now(),INTERVAL1month));25# 下個月最后一天是幾號26setnext_month_day=dayofmonth(LAST_DAY(date_add(now(),INTERVAL1month)));2728# 如果下一個月月份小于10,就在月份的前面拼接一個029ifnext_month<1030thensetnext_month_str=concat('0',next_month);31else32# 如果月份大于10,不做任何操作33setnext_month_str=concat('',next_month);34endif;3536# 循環操作(下個月的日大于等于1循環開始循環)37whilet_index<=next_month_daydo3839# 如果t_index小于10就在前面拼接040if(t_index<10)41thensetnext_month_day_str=concat('0',t_index);42else43# 如果t_index大于10不做任何操作44setnext_month_day_str=concat('',t_index);45endif;4647# 拼接標命字符串48settable_name_str=concat(next_year,'_',next_month_str,'_',next_month_day_str);49# 拼接create sql語句50set@create_table_sql=concat(51'create table comp_',52table_name_str,53'(`grade` INT(11) NULL,`losal` INT(11) NULL,`hisal` INT(11) NULL) COLLATE=\'utf8_general_ci\' ENGINE=InnoDB');54# 預編譯55# 注意:FROM后面不能使用局部變量!56preparecreate_table_stmtFROM@create_table_sql;57# 執行58executecreate_table_stmt;59# 創建表60DEALLOCATEpreparecreate_table_stmt;6162# t_index自增163sett_index=t_index+1;6465endwhile;66end//6768# 調用函數69callsp_create_table()
七、其他
7.1 characteristic
在MySQL存儲過程中,如果沒有顯示的定義characteristic,它會隱式的定義一系列特性的默認值來創建存儲過程。
LANGUAGE SQL
存儲過程語言,默認是sql,說明存儲過程中使用的是sql語言編寫的,暫時只支持sql,后續可能會支持其他語言
NOT DETERMINISTIC
是否確定性的輸入就是確定性的輸出,默認是NOT DETERMINISTIC,只對于同樣的輸入,輸出也是一樣的,當前這個值還沒有使用
CONTAINS SQL
提供子程序使用數據的內在信息,這些特征值目前提供給服務器,并沒有根據這些特征值來約束過程實際使用數據的情況。有以下選擇:CONTAINS SQL表示子程序不包含讀或者寫數據的語句NO SQL 表示子程序不包含sqlREADS SQL DATA 表示子程序包含讀數據的語句,但是不包含寫數據的語句MODIFIES SQL DATA 表示子程序包含寫數據的語句。
SQL SECURITY DEFINER
MySQL存儲過程是通過指定SQL SECURITY子句指定執行存儲過程的實際用戶。所以次值用來指定存儲過程是使用創建者的許可來執行,還是執行者的許可來執行,默認值是DEFINERDEFINER 創建者的身份來調用,對于當前用戶來說:如果執行存儲過程的權限,且創建者有訪問表的權限,當前用戶可以成功執行過程的調用的INVOKER 調用者的身份來執行,對于當前用戶來說:如果執行存儲過程的權限,以當前身份去訪問表,如果當前身份沒有訪問表的權限,即便是有執行過程的權限,仍然是無法成功執行過程的調用的。
COMMENT ''
存儲過程的注釋性信息寫在COMMENT里面,這里只能是單行文本,多行文本會被移除到回車換行等
7.2 死循環處理
如有死循環處理,可以通過下面的命令查看并殺死(結束)
1showprocesslist;2killid;
7.3 select語句中書寫case
1select2case3when 條件判斷 then 結果4when 條件判斷 then 結果5else 結果6end 別名,7*8from表名;
7.4 復制表和數據
1CREATE TABLE deptSELECT*FROMprocedure_demo.dept;2CREATE TABLE empSELECT*FROMprocedure_demo.emp;3CREATE TABLE salgradeSELECT*FROMprocedure_demo.salgrade;
7.5 臨時表
1create temporary table 表名( 2 字段名 類型 [約束], 3 name varchar(20)? 4)Engine=InnoDB default charset utf8; 5 6-- 需求:按照部門名稱查詢員工,通過select查看員工的編號、姓名、薪資。(注意,此處僅僅演示游標用法)7delimiter $$ 8create procedure sp_create_table02(in dept_name varchar(32)) 9begin10declareemp_noint;11declareemp_namevarchar(32);12declareemp_saldecimal(7,2);13declareexit_flagintdefault0;1415declareemp_cursorcursorfor16selecte.empno,e.ename,e.sal17fromemp einnerjoindept done.deptno = d.deptnowhered.dname = dept_name;1819declarecontinuehandlerfornotfoundsetexit_flag =1;2021-- 創建臨時表收集數據22CREATEtemporaryTABLE`temp_table_emp`(23`empno`INT(11)NOTNULLCOMMENT'員工編號',24`ename`VARCHAR(32)NULLCOMMENT'員工姓名'COLLATE'utf8_general_ci',25`sal`DECIMAL(7,2)NOTNULLDEFAULT'0.00'COMMENT'薪資',26PRIMARYKEY(`empno`)USINGBTREE27)28COLLATE='utf8_general_ci'29ENGINE=InnoDB;? 3031? ? open emp_cursor;3233? ? c_loop:loop34? ? ? ? fetch emp_cursor into emp_no,emp_name,emp_sal;353637? ? ? ? if exit_flag != 1 then38insertintotemp_table_empvalues(emp_no,emp_name,emp_sal); 39? ? ? ? else40? ? ? ? ? ? leave c_loop;41endif;4243endloopc_loop;4445select*fromtemp_table_emp;4647select@sex_res;-- 僅僅是看一下會不會執行到48? ? close emp_cursor;4950end$$5152call sp_create_table02('RESEARCH');
原文鏈接:https://www.tuicool.com/articles/INf2qe3