天天看點

面試官突然問我MySQL存儲過程,我竟然連基礎都不會!(詳細)

面試官突然問我MySQL存儲過程,我竟然連基礎都不會!(詳細)

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存儲過程的各個知識點。

1

https://dev.mysql.com/doc/refman/5.6/en/preface.html

2.3 存儲過程的使用文法

1create PROCEDURE 過程名( in|out|inout 參數名 資料類型 , ...)

2begin

3    sql語句;

4end;

5call 過程名(參數值);

in是定義傳入參數的關鍵字。out是定義出參的關鍵字。inout是定義一個出入參數都可以的參數。如果括号内什麼都不定義,就說明該存儲過程時一個無參的函數。在後面會有詳細的案例分析。

注意: SQL語句預設的結束符為;,是以在使用以上存儲過程時,會報1064的文法錯誤。我們可以使用DELIMITER關鍵字臨時聲明修改SQL語句的結束符為//,如下:

1-- 臨時定義結束符為"//"

2DELIMITER //

3create PROCEDURE 過程名( in|out 參數名 資料類型 , ...)

4begin

5    sql語句;

6end//

7-- 将結束符重新定義回結束符為";"

8DELIMITER ;

例如: 使用存儲過程來查詢員工的工資(無參)

注意: 如果在特殊的必要情況下,我們還可以通過delimiter關鍵字将;結束符聲明回來使用,在以下案例中我并沒有這樣将結束符聲明回原來的;,在此請大家注意~

為什麼我在這裡提供了drop(删除)呢?

是因為我們在使用的時候如果需要修改存儲過程中的内容,我們需要先删除現有的存儲過程後,再creat重新建立。

1# 聲明結束符為//

2delimiter //

3

4# 建立存儲過程(函數)

5create procedure se()

6begin

7    select salary from employee;

8end //

9

10# 調用函數

11call se() //

12

13# 删除已存在存儲過程——se()函數

14drop procedure if exists se //

三、存儲過程的變量和指派

3.1 局部變量

聲明局部變量文法: declare var_name type [default var_value];

指派文法:

注意: 局部變量的定義,在begin/end塊中有效。

使用set為參數指派

1# set指派

2

3# 聲明結束符為//

4delimiter //

5

6# 建立存儲過程

7create procedure val_set()

8begin

9    # 聲明一個預設值為unknown的val_name局部變量

10    declare val_name varchar(32) default 'unknown';

11    # 為局部變量指派

12    set val_name = 'Centi';

13    # 查詢局部變量

14    select val_name;

15end //

16

17# 調用函數

18call val_set() //

19

使用into接收參數

1delimiter //

2create procedure val_into()

3begin

4    # 定義兩個變量存放name和age

5    declare val_name varchar(32) default 'unknown';

6    declare val_age int;

7    # 查詢表中id為1的name和age并放在定義的兩個變量中

8    select name,age into val_name,val_age from employee where id = 1;

9    # 查詢兩個變量

10    select val_name,val_age;

11end //

13call val_into() //

14

3.2 使用者變量

使用者自定義使用者變量,目前會話(連接配接)有效。與Java中的成員變量相似。

文法: @val_name

注意: 該使用者變量不需要提前聲明,使用即為聲明。

1delimiter //

2create procedure val_user()

4    # 為使用者變量指派

5    set @val_name = 'Lacy';

6end //

7

8# 調用函數

9call val_user() //

10

11# 查詢該使用者變量

12select @val_name //

3.3 會話變量

會話變量是由系統提供的,隻在目前會話(連接配接)中有效。

文法: @@session.val_name

1# 檢視所有會話變量

2show session variables;

3# 檢視指定的會話變量

4select @@session.val_name;

5# 修改指定的會話變量

6set @@session.val_name = 0;

這裡我擷取了一下所有的會話變量,大概有500條會話變量的記錄。等我們深入學習MySQL後,了解了各個會話變量值的作用,可以根據需求和場景來修改會話變量值。

2create procedure val_session()

3begin

4    # 檢視會話變量

5    show session variables;

6end //

8call val_session() //

image-20200610112512964

3.4 全局變量

全局變量由系統提供,整個MySQL伺服器内有效。

文法: @@global.val_name

1# 檢視全局變量中變量名有char的記錄

2show global variables like '%char%' //

3# 檢視全局變量character_set_client的值

4select @@global.character_set_client //

3.5 入參出參

入參出參的文法我們在文章開頭已經提過了,但是沒有示範,在這裡我将示範一下入參出參的使用。

文法: in|out|inout 參數名 資料類型 , ...

in定義出參;out定義入參;inout定義出參和入參。

出參in

使用出參in時,就是需要我們傳入參數,在這裡可以對參入的參數加以改變。簡單來說in隻負責傳入參數到存儲過程中,類似Java中的形參。

2create procedure val_in(in val_name varchar(32))

4    # 使用使用者變量出參(為使用者變量賦參數值)

5    set @val_name1 = val_name;

9call val_in('DK') //

12select @val_name1 //

入參out

在使用out時,需要傳入一個參數。而這個參數相當于是傳回值,可以通過調用、接收來擷取這個參數的内容。簡單來說out隻負責作傳回值。

2# 建立一個入參和出參的存儲過程

3create procedure val_out(in val_id int,out val_name varchar(32))

4begin

5    # 傳入參數val_id查詢員工傳回name值(查詢出的name值用出參接收并傳回)

6    select name into val_name from employee where id = val_id;

7end //

8

9# 調用函數傳入參數并聲明傳入一個使用者變量

10call val_out(1, @n) //

11

12# 查詢使用者變量

13select @n //

入參出參inout

inout關鍵字,就是把in和out合并成了一個關鍵字使用。被關鍵字修飾的參數既可以出參也可以入參。

2create procedure val_inout(in val_name varchar(32), inout val_age int)

4    # 聲明一個a變量

5    declare a int;

6    # 将傳入的參數指派給a變量

7    set a = val_age;

8    # 通過name查詢age并傳回val_age

9    select age into val_age from employee where name = val_name;

10    # 将傳入的a與-和查詢age結果字元串做拼接并查詢出來(concat——拼接字元串)

11    select concat(a, '-', val_age);

12end //

13

14# 聲明一個使用者變量并賦予參數為40

15set @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 結果]

4END IF

舉例: 傳入所查詢的id參數查詢工資标準(s<=6000為低工資标準;6000=15000為高工資标準)<=10000為中工資标準;10000

2create procedure s_sql(in val_id int)

4    # 聲明一個局部變量result存放工資标準結果

5    declare result varchar(32);

6    # 聲明一個局部變量存放查詢得到的工資

7    declare s double;

8    # 根據入參id查詢工資

9    select salary into s from employee where id = val_id;

10    # if判斷的使用

11    if s <= 6000 then

12        set result = '低工資标準';

13    elseif s <= 10000 then

14        set result = '中工資标準';

15    elseif s <= 15000 then

16        set result = '中上工資标準';

17    else

18        set result = '高工資标準';

19    end if;

20    # 查詢工資标準結果

21    select result;

22end //

23

24# 調用函數,傳入參數

25call s_sql(1);

4.2 case條件判斷

關于case語句,不僅僅在存儲過程中可以使用,MySQL基礎查詢語句中也有用到過。相當于是Java中的switch語句。

1# 文法一

2CASE case_value

3    WHEN when_value THEN 結果

4    [WHEN when_value THEN 結果] ...

5    [ELSE 結果]

6END CASE

8# 文法二(推薦文法)

9CASE

10    WHEN 條件判斷 THEN 結果

11    [WHEN 條件判斷 THEN 結果] ...

12    [ELSE 結果]

13END CASE

舉例:

3create procedure s_case(in val_id int)

5    # 聲明一個局部變量result存放工資标準結果

6    declare result varchar(32);

7    # 聲明一個局部變量存放查詢得到的工資

8    declare s double;

9    # 根據入參id查詢工資

10    select salary into s from employee where id = val_id;

11    case s

12        when 6000 then set result = '低工資标準';

13        when 10000 then set result = '中工資标準';

14        when 15000 then set result = '中上工資标準';

15        else set result = '高工資标準';

16    end case;

17    select result;

18end //

20call s_case(1);

21

22# 文法二(推薦)

23delimiter //

24create procedure s_case(in val_id int)

25begin

26    # 聲明一個局部變量result存放工資标準結果

27    declare result varchar(32);

28    # 聲明一個局部變量存放查詢得到的工資

29    declare s double;

30    # 根據入參id查詢工資

31    select salary into s from employee where id = val_id;

32    case

33        when s <= 6000 then set result = '低工資标準';

34        when s <= 10000 then set result = '中工資标準';

35        when s <= 15000 then set result = '中上工資标準';

36        else set result = '高工資标準';

37    end case;

38    select result;

39end //

40

41call s_case(1);

4.3 loop循環

loop為死循環,需要手動退出循環,我們可以使用leave來退出循環

可以把leave看成Java中的break;與之對應的,就有iterate(繼續循環)也可以看成Java的continue

1[别名:] LOOP

2    循環語句

3END LOOP [别名]

注意:别名和别名控制的是同一個标簽。

示例1: 循環列印1~10(leave控制循環的退出)

注意:該loop循環為死循環,我們查的1~10數字是i,在死循環中設定了當大于等于10時停止循環,也就是說先後執行了10次該循環内的内容,結果查詢了10次,生成了10個結果(1~10)。

2create procedure s_loop()

4    # 聲明計數器

5    declare i int default 1;

6    # 開始循環

7    num:

8    loop

9        # 查詢計數器記錄的值

10        select i;

11        # 判斷大于等于停止計數

12        if i >= 10 then

13            leave num;

14        end if;

15        # 計數器自增1

16        set i = i + 1;

17    # 結束循環

18    end loop num;

19end //

20

21call s_loop();

列印結果:

image-20200610191639524

示例2: 循環列印1~10(iterate和leave控制循環)

注意:這裡我們使用字元串拼接計數器結果,而條件如果用iterate就必須時 i < 10 了!

2create procedure s_loop1()

4    # 聲明變量i計數器

6    # 聲明字元串容器

7    declare str varchar(256) default '1';

8    # 開始循環

9    num:

10    loop

11        # 計數器自增1

12        set i = i + 1;

13        # 字元串容器拼接計數器結果

14        set str = concat(str, '-', i);

15        # 計數器i如果小于10就繼續執行

16        if i < 10 then

17            iterate num;

18        end if;

19        # 計數器i如果大于10就停止循環

20        leave num;

21    # 停止循環

22    end loop num;

23    # 查詢字元串容器的拼接結果

24    select str;

25end //

26

27call s_loop1();

image-20200610193153512

4.4 repeat循環

repeat循環類似Java中的do while循環,直到條件不滿足才會結束循環。

1[别名:] REPEAT

3UNTIL 條件

4END REPEAT [别名]

示例: 循環列印1~10

2create procedure s_repeat()

4    declare i int default 1;

5    declare str varchar(256) default '1';

6    # 開始repeat循環

8    repeat

9        set i = i + 1;

10        set str = concat(str, '-', i);

11    # until 結束條件

12    # end repeat 結束num 結束repeat循環

13    until i >= 10 end repeat num;

14    # 查詢字元串拼接結果

15    select str;

16end //

17

18call s_repeat();

4.5 while循環

while循環就與Java中的while循環很相似了。

1[别名] WHILE 條件 DO

3END WHILE [别名]

2create procedure s_while()

6    # 開始while循環

8    # 指定while循環結束條件

9    while i < 10 do

10        set i = i + 1;

11        set str = concat(str, '+', i);

12    # while循環結束

13    end while num;

14    # 查詢while循環拼接字元串

18call s_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。

2create procedure f()

4    declare val_id int;

5    declare val_name varchar(32);

6    declare val_salary double;

8    # 聲明遊标

9    declare emp_flag cursor for

10    select id, name, salary from employee;

12    # 打開

13    open emp_flag;

15    # 取值

16    fetch emp_flag into val_id, val_name, val_salary;

18    # 關閉

19    close emp_flag;

21    select val_id, val_name, val_salary;

24call f();

執行結果:

image-20200610203622749

因為遊标逐行操作的特點,導緻我們隻能使用遊标來查詢一行記錄。怎麼改善代碼才可以實作查詢所有記錄呢?聰明的小夥伴想到了使用循環。對,我們試試使用一下循環。

15    # 使用循環取值

16    c:loop

17        # 取值

18        fetch emp_flag into val_id, val_name, val_salary;

19    end loop;

21    # 關閉

22    close emp_flag;

24    select val_id, val_name, val_salary;

27call f();

image-20200610204034224

我們使用循環之後,發現有一個問題,因為循環是死循環,我們不加結束循環的條件,遊标會一直查詢記錄,當查到沒有的記錄的時候,就會抛出異常1329:未擷取到選擇處理的行數。

如果我們想辦法指定結束循環的條件該怎麼做呢?

這時候可以聲明一個boolean類型的标記。如果為true時則查詢結果集,為false時則結束循環。

8    # 聲明flag标記

9    declare flag boolean default true;

11    # 聲明遊标

12    declare emp_flag cursor for

13    select id, name, salary from employee;

15    # 打開

16    open emp_flag;

18    # 使用循環取值

19    c:loop

20        fetch emp_flag into val_id, val_name, val_salary;

21        # 如果标記為true則查詢結果集

22        if flag then

23            select val_id, val_name, val_salary;

24        # 如果标記為false則證明結果集查詢完畢,停止死循環

25        else

26            leave c;

27        end if;

28    end loop;

29

30    # 關閉

31    close emp_flag;

32

33    select val_id, val_name, val_salary;

34end //

35

36call f();

上述代碼你會發現并沒有寫完,它留下了一個很嚴肅的問題。當flag = false時候可以結束循環。但是什麼時候才讓flag為false啊?

于是,MySQL為我們提供了一個handler句柄。它可以幫我們解決此疑惑。

handler句柄文法: declare continue handler for 異常 set flag = false;

handler句柄可以用來捕獲異常,也就是說在這個場景中當捕獲到1329:未擷取到選擇處理的行數時,就将flag标記的值改為false。這樣使用handler句柄就解決了結束循環的難題。讓我們來試試吧!

終極版示例: 解決了多行查詢以及結束循環問題。

15    # 使用handler句柄來解決結束循環問題

16    declare continue handler for 1329 set flag = false;

18    # 打開

19    open emp_flag;

21    # 使用循環取值

22    c:loop

23        fetch emp_flag into val_id, val_name, val_salary;

24        # 如果标記為true則查詢結果集

25        if flag then

26            select val_id, val_name, val_salary;

27        # 如果标記為false則證明結果集查詢完畢,停止死循環

28        else

29            leave c;

30        end if;

31    end loop;

33    # 關閉

34    close emp_flag;

36    select val_id, val_name, val_salary;

37end //

38

39call f();

image-20200610210925964

在執行結果中,可以看出查詢結果以多次查詢的形式,分布顯示到了每一個查詢結果視窗中。

注意: 在文法中,變量聲明、遊标聲明、handler聲明是必須按照先後順序書寫的,否則建立存儲過程出錯。

5.2 handler句柄

1DECLARE handler操作 HANDLER

2    FOR 情況清單...(比如:異常錯誤情況)

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

寫法示例:

1    DECLARE exit HANDLER FOR SQLSTATE '3D000' set flag = false;

2    DECLARE continue HANDLER FOR 1050 set flag = false;

3    DECLARE continue HANDLER FOR not found set flag = 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語句。

3create procedure sp_create_table()

5    # 聲明需要拼接表名的下一個月的年、月、日

6    declare next_year int;

7    declare next_month int;

8    declare next_month_day int;

10    # 聲明下一個月的月和日的字元串

11    declare next_month_str char(2);

12    declare next_month_day_str char(2);

14    # 聲明需要處理每天的表名

15    declare table_name_str char(10);

17    # 聲明需要拼接的1

18    declare t_index int default 1;

19    # declare create_table_sql varchar(200);

21    # 擷取下個月的年份

22    set next_year = year(date_add(now(),INTERVAL 1 month));

23    # 擷取下個月是幾月 

24    set next_month = month(date_add(now(),INTERVAL 1 month));

25    # 下個月最後一天是幾号

26    set next_month_day = dayofmonth(LAST_DAY(date_add(now(),INTERVAL 1 month)));

27

28    # 如果下一個月月份小于10,就在月份的前面拼接一個0

29    if next_month < 10

30        then set next_month_str = concat('0',next_month);

31    else

32        # 如果月份大于10,不做任何操作

33        set next_month_str = concat('',next_month);

34    end if;

36    # 循環操作(下個月的日大于等于1循環開始循環)

37    while t_index <= next_month_day do

39        # 如果t_index小于10就在前面拼接0

40        if (t_index < 10)

41            then set next_month_day_str = concat('0',t_index);

42        else

43            # 如果t_index大于10不做任何操作

44            set next_month_day_str = concat('',t_index);

45        end if;

46

47        # 拼接标命字元串

48        set table_name_str = concat(next_year,'_',next_month_str,'_',next_month_day_str);

49        # 拼接create sql語句

50        set @create_table_sql = concat(

51                    'create table comp_',

52                    table_name_str,

53                    '(

grade

 INT(11) NULL,

losal

hisal

 INT(11) NULL) COLLATE='utf8_general_ci' ENGINE=InnoDB');

54        # 預編譯

55        # 注意:FROM後面不能使用局部變量!

56        prepare create_table_stmt FROM @create_table_sql;

57        # 執行

58        execute create_table_stmt;

59        # 建立表

60        DEALLOCATE prepare create_table_stmt;

61

62        # t_index自增1

63        set t_index = t_index + 1;

64

65    end while;  

66end//

67

68# 調用函數

69call sp_create_table()

七、其他

7.1 characteristic

在MySQL存儲過程中,如果沒有顯示的定義characteristic,它會隐式的定義一系列特性的預設值來建立存儲過程。

LANGUAGE SQL

存儲過程語言,預設是sql,說明存儲過程中使用的是sql語言編寫的,暫時隻支援sql,後續可能會支援其他語言

NOT DETERMINISTIC

是否确定性的輸入就是确定性的輸出,預設是NOT DETERMINISTIC,隻對于同樣的輸入,輸出也是一樣的,目前這個值還沒有使用

CONTAINS SQL

提供子程式使用資料的内在資訊,這些特征值目前提供給伺服器,并沒有根據這些特征值來限制過程實際使用資料的情況。有以下選擇:

CONTAINS SQL表示子程式不包含讀或者寫資料的語句

NO SQL 表示子程式不包含sql

READS SQL DATA 表示子程式包含讀資料的語句,但是不包含寫資料的語句

MODIFIES SQL DATA 表示子程式包含寫資料的語句。

SQL SECURITY DEFINER

MySQL存儲過程是通過指定SQL SECURITY子句指定執行存儲過程的實際使用者。是以次值用來指定存儲過程是使用建立者的許可來執行,還是執行者的許可來執行,預設值是DEFINER

DEFINER 建立者的身份來調用,對于目前使用者來說:如果執行存儲過程的權限,且建立者有通路表的權限,目前使用者可以成功執行過程的調用的

INVOKER 調用者的身份來執行,對于目前使用者來說:如果執行存儲過程的權限,以目前身份去通路表,如果目前身份沒有通路表的權限,即便是有執行過程的權限,仍然是無法成功執行過程的調用的。

COMMENT ''

存儲過程的注釋性資訊寫在COMMENT裡面,這裡隻能是單行文本,多行文本會被移除到回車換行等

7.2 死循環處理

如有死循環處理,可以通過下面的指令檢視并殺死(結束)

1show processlist;

2kill id;

7.3 select語句中書寫case

1select 

2    case

3        when 條件判斷 then 結果

4        when 條件判斷 then 結果

5        else 結果

6    end 别名,

7    *

8from 表名;

7.4 複制表和資料

1CREATE TABLE dept SELECT * FROM procedure_demo.dept;

2CREATE TABLE emp SELECT * FROM procedure_demo.emp;

3CREATE TABLE salgrade SELECT * FROM procedure_demo.salgrade;

7.5 臨時表

1create temporary table 表名(

2  字段名 類型 [限制],

3  name varchar(20) 

4)Engine=InnoDB default charset utf8;

6-- 需求:按照部門名稱查詢員工,通過select檢視員工的編号、姓名、薪資。(注意,此處僅僅示範遊标用法)

7delimiter $$

8create procedure sp_create_table02(in dept_name varchar(32))

9begin

10    declare emp_no int;

11    declare emp_name varchar(32);

12    declare emp_sal decimal(7,2);

13    declare exit_flag int default 0;

15    declare emp_cursor cursor for

16        select e.empno,e.ename,e.sal

17        from emp e inner join dept d on e.deptno = d.deptno where d.dname = dept_name;

18

19    declare continue handler for not found set exit_flag = 1;

21    -- 建立臨時表收集資料

22    CREATE temporary TABLE 

temp_table_emp

 (

23        

empno

 INT(11) NOT NULL COMMENT '員工編号',

24        

ename

 VARCHAR(32) NULL COMMENT '員工姓名' COLLATE 'utf8_general_ci',

25        

sal

 DECIMAL(7,2) NOT NULL DEFAULT '0.00' COMMENT '薪資',

26        PRIMARY KEY (

empno

) USING BTREE

27    )

28    COLLATE='utf8_general_ci'

29    ENGINE=InnoDB;  

30

31    open emp_cursor;

33    c_loop:loop

34        fetch emp_cursor into emp_no,emp_name,emp_sal;

36

37        if exit_flag != 1 then

38            insert into temp_table_emp values(emp_no,emp_name,emp_sal); 

39        else

40            leave c_loop;

41        end if;

42

43    end loop c_loop;

44

45    select * from temp_table_emp;

47    select @sex_res; -- 僅僅是看一下會不會執行到

48    close emp_cursor;

49

50end$$

51

52call sp_create_table02('RESEARCH');

原文位址

https://www.cnblogs.com/ziph/p/13090117.html