mysql存儲過程筆記,詳細且易懂

一、定義

       存儲過程(Stored Procedure)是在大型數據庫系統中,一組爲了完成特定功能的SQL 語句集,存儲在數據庫中,經過第一次編譯後調用不需要再次編譯,用戶通過指定存儲過程的名字並給出參數(如果該存儲過程帶有參數)來執行它。存儲過程是數據庫中的一個重要對象。

二、存儲過程的特點

    1、能完成較複雜的判斷和運算
    2、可編程行強,靈活
    3、SQL編程的代碼可重複使用
    4、執行的速度相對快一些
    5、減少網絡之間的數據傳輸,節省開銷    

三、創建一個簡單的存儲過程

    1、創建存儲過程的簡單語法

create procedure 名稱()
begin
.........
end

    2、創建一個簡單的存儲過程

create procedure testa()
begin
    select * from users;
    select * from orders;
end;

    3、調用存儲過程

call testa();   

運行結果如圖(1)和圖(2):

                                 圖(1)

 

                                 圖(2)

四、存儲過程的變量

    1、先通過一個簡單的例子來學習變量的聲明和賦值

create procedure test2()
begin
  -- 使用 declare語句聲明一個變量
  declare username varchar(32) default '';
  -- 使用set語句給變量賦值
  set username='xiaoxiao';
  -- 將users表中id=1的名稱賦值給username
  select name into username from users where id=1;
  -- 返回變量
  select username;
end;

    2、概括
        (1)、變量的聲明使用declare,一句declare只聲明一個變量,變量必須先聲明後使用;

        (2)、變量具有數據類型和長度,與mysql的SQL數據類型保持一致,因此甚至還能制定默認值、字符集和排序規則等;
        (3)、變量可以通過set來賦值,也可以通過select into的方式賦值;
        (4)、變量需要返回,可以使用select語句,如:select 變量名。
       

五、變量的作用域

    1、變量作用域說明:
        (1)、存儲過程中變量是有作用域的,作用範圍在begin和end塊之間,end結束變量的作用範圍即結束。
        (2)、需要多個塊之間傳值,可以使用全局變量,即放在所有代碼塊之前
        (3)、傳參變量是全局的,可以在多個塊之間起作用
    2、通過一個實例來驗證變量的作用域

         需求: 創建一個存儲過程,用來統計表users、orders表中行數數量和orders表中的最大金額和最小金額

create procedure test3()
begin
  begin
    declare userscount int default 0; -- 用戶表中的數量
    declare ordercount int default 0; -- 訂單表中的數量
    select count(*) into userscount from users;
    select count(*) into ordercount from orders;
    select userscount,ordercount; -- 返回用戶表中的數量、訂單表中的數量
  end;
  begin 
    declare maxmoney int default 0; -- 最大金額
    declare minmoney int default 0; -- 最小金額
    select max(money) into maxmoney from orders;
    select min(money) into minmoney from orders;
    select maxmoney,minmoney; -- 返回最金額、最小金額
   end;
end;

調用以上存儲過程,結果如圖(3)和圖(4):

           

                   (3)    

     
                       (4)

    3、我將過程test(3)改爲如下:

create procedure test3()
    begin
      begin
        declare userscount int default 0; -- 用戶表中的數量
        declare ordercount int default 0; -- 訂單表中的數量
        select count(*) into userscount from users;
        select count(*) into ordercount from orders;
        select userscount,ordercount; -- 返回用戶表中的數量、訂單表中的數量
      end;
      begin 
        declare maxmoney int default 0; -- 最大金額
        declare minmoney int default 0; -- 最小金額
        select max(money) into maxmoney from orders;
        select min(money) into minmoney from orders;
        select userscount,ordercount,maxmoney,minmoney; -- 返回最金額、最小金額
       end;
    end;

再次調用call test3(); 會報錯如圖(5):

                               圖(5)

    4、將userscount,ordercount改爲全局變量,再次驗證

create procedure test3()
    begin

        declare userscount int default 0; -- 用戶表中的數量
        declare ordercount int default 0; -- 訂單表中的數量
        begin
            select count(*) into userscount from users;
            select count(*) into ordercount from orders;
            select userscount,ordercount; -- 返回用戶表中的數量、訂單表中的數量
      end;
      begin 
        declare maxmoney int default 0; -- 最大金額
        declare minmoney int default 0; -- 最小金額
        select max(money) into maxmoney from orders;
        select min(money) into minmoney from orders;
        select userscount,ordercount,maxmoney,minmoney; -- 返回最金額、最小金額
       end;
    end;

再次調用call test3(); 會報錯如圖(6)和圖(7):

       

                    圖(6)       

                                                             

                    圖(7)

因此,存儲過程中變量的作用域,作用範圍在begin和end塊之間,end結束變量的作用範圍即結束
    

六、存儲過程參數


  1、基本語法

create procedure 名稱([IN|OUT|INOUT] 參數名 參數數據類型 )
begin
.........
end

 存儲過程的參數類型有:IN,OUT,INOUT,下面分別介紹這個三種類型:
    
  2、存儲過程的傳出參數IN

     說明:        

        (1)、傳入參數:類型爲in,表示該參數的值必須在調用存儲過程事指定,如果不顯示指定爲in,那麼默認就是in類型。
        (2)、IN類型參數一般只用於傳入,在調用過程中一般不作爲修改和返回
        (3)、如果調用存儲過程中需要修改和返回值,可以使用OUT類型參數

通過一個實例來演示:

需求:編寫存儲過程,傳入id,根據id返回name

 create procedure test4(userId int)
    begin
            declare username varchar(32) default '';
            declare ordercount int default 0;
            select name into username from users where id=userId;
            select username;
    end;

運行如圖(8)


    

                                                       圖(8)
  
    
   3、存儲過程的傳出參數out
    
        需求:調用存儲過程時,傳入userId返回該用戶的name

 create procedure test5(in userId int,out username varchar(32))
        begin
            select name into username from users where id=userId;
        end;

        調用及運行結果如圖(9):

                                   圖(9)
        
  概括:
        1、傳出參數:在調用存儲過程中,可以改變其值,並可返回;
        2、out是傳出參數,不能用於傳入參數值;
        3、調用存儲過程時,out參數也需要指定,但必須是變量,不能是常量
        4、如果既需要傳入,同時又需要傳出,則可以使用INOUT類型參數
    
    (3).存儲過程的可變參數INOUT
    
        需求:調用存儲過程時,傳入userId和userName,即使傳入,也是傳出參數。

create procedure test6(inout userId int,inout username varchar(32))
begin
    set userId=2;
    set username='';
    select id,name into userId,username from users where id=userId;
end;

 調用及運行結果如圖(10)


        

                                       圖(10)
    概括:
        1、可變變量INOUT:調用時可傳入值,在調用過程中,可修改其值,同時也可返回值;
        2、INOUT參數集合了IN和OUT類型的參數功能;
        3、INOUT調用時傳入的是變量,而不是常量;

 

七、存儲過程條件語句

   1、基本結構

   (1)、條件語句基本結構:

if() then...else...end if;

   (2)、多條件判斷語句:

if() then...
elseif() then...
else ...
end if;

   2、實例
    實例1:編寫存儲過程,如果用戶userId是偶數則返回username,否則返回userId

create procedure test7(in userId int)
begin
   declare username varchar(32) default '';
   if(userId%2=0)
   then 
      select name into username from users where id=userId;
      select username;
   else
      select userId;
      end if;
end;

    調用及運行結果如圖(11)和圖(12):

       

                        圖(11)           

                                     

                               圖(12)
    
    2、存儲過程的多條件語句應用示例
        需求:根據用戶傳入的uid參數判斷
        (1)、如果用戶狀態status爲1,則給用戶score加10分;
        (2)、 如果用戶狀態status爲2,則給用戶score加20分;
        (3)、 其他情況加30分

create procedure test8(in userid int)
begin
   declare my_status int default 0;
   select status into my_status from users where id=userid;
   if(my_status=1)
   then 
       update users set score=score+10 where id=userid;
    elseif(my_status=2)
    then 
       update users set score=score+20 where id=userid;
    else 
       update users set score=score+30 where id=userid;
    end if;
end;

調用過程之前的users表的數據如圖(13),調用 call test8(1); 及運行結果圖(14):


           
                                   圖(13)       

     

                                    圖(14)
      

八、存儲過程循環語句


    1、while語句

       (1)、while語句的基本結構

while(表達式) do 
   ......  
end while;

         (2)、示例
    需求:使用循環語句,向表test1(id)中插入10條連續的記錄

create procedure test9()
begin
  declare i int default 0;
  while(i<10) do 
    begin 
        select i;
        set i=i+1;
        insert into test1(id) values(i);
     end;
  end while;
end;

    調用及運行結果結果如圖(15)和圖(16):


    

                                                                    圖(15)

   

         圖(16)


    2、repeat語句
    (1)、repeat語句基本的結構:

repeat...until...end repeat;

     (2)、示例

需求:給test1表中的id字段插入數據,從1到10

create procedure test10()
begin
    declare i int default 0;
    repeat 
    begin 
        select i;
        set i=i+1;
        insert into test1(id) values(i);
    end;
    until i>=10 -- 如果i>=10,則跳出循環
    end repeat;
end;

   調用及運行結果結果如圖(17)和圖(18)

   

    圖(17)             

   

              圖(18)
        
    概括:
        until判斷返回邏輯真或者假,表達式可以是任意返回真或者假的表達式,只有當until語句爲真是,循環結束。
        

九、存儲過程遊標的使用


    1、什麼是遊標
        遊標是保存查詢結果的臨時區域
    2、示例
    需求:編寫存儲過程,使用遊標,把users表中 id爲偶數的記錄逐一更新用戶名
    

create procedure test11()
    begin
        declare stopflag int default 0;
        declare username VARCHAR(32);
        -- 創建一個遊標變量,declare 變量名 cursor ...
        declare username_cur cursor for select name from users where id%2=0;
        -- 遊標是保存查詢結果的臨時區域
        -- 遊標變量username_cur保存了查詢的臨時結果,實際上就是結果集
        -- 當遊標變量中保存的結果都查詢一遍(遍歷),到達結尾,將變量stopflag設置爲1,用於循環中判斷是否結束
        declare continue handler for not found set stopflag=1;
 
        open username_cur; -- 打卡遊標
        fetch username_cur into username; -- 遊標向前走一步,取出一條記錄放到變量username中
        while(stopflag=0) do -- 如果遊標還沒有結尾,就繼續
            begin 
                -- 在用戶名前門拼接 '_cur' 字符串
                update users set name=CONCAT(username,'_cur') where name=username;
                fetch username_cur into username;
            end;
        end while; -- 結束循環
        close username_cur; -- 關閉遊標
    end;

調用結果如圖(19):

                                     圖(19)


十、自定義函數


    函數與存儲過程最大的區別是函數必須有返回值,否則會報錯
    
    1、一個簡單的函數

create function getusername(userid int) returns varchar(32)
    reads sql data  -- 從數據庫中讀取數據,但不修改數據
    begin
        declare username varchar(32) default '';
        select name into username from users where id=userid;
        return username;
    end;

    
    調用及運行結果如圖(20):

                圖(20)
    
    概括:
    1.創建函數使用create function 函數名(參數) returns 返回類型;
    2.函數體放在begin和end之間;
    3.returns指定函數的返回值;
    4.函數調用使用select getusername()。
    
    2、示例
    需求:根據userid,獲取accoutid,id,name組合成UUID作爲用戶的唯一標識

  create function getuuid(userid int) returns varchar(64)
    reads sql data  -- 從數據庫中讀取數據,但不修改數據
    begin
        declare uuid varchar(64) default '';
        select concat(accontid,'_',id,'_',name) into uuid from users where id=userid;
        return uuid;
    end;

調用及運行結果如圖(21)

             圖(21)

   
十一、觸發器


    1、什麼是觸發器


    觸發器與函數、存儲過程一樣,觸發器是一種對象,它能根據對錶的操作時間,觸發一些動作,這些動作可以是insert,update,delete等修改操作。

    2、示例1

(1)、需求:出於審計目的,當有人往表users插入一條記錄時,把插入的userid,username,插入動作和操作時間記錄下來。
    

create trigger tr_users_insert after insert on users
    for each row 
    begin 
        insert into oplog(userid,username,action,optime)
        values(NEW.id,NEW.name,'insert',now());
    end;


    創建成功後,給uses表中插入一條記錄:
    

insert into users(id,name,age,status,score,accontid)
    values(6,'小周',23,1,'60','10001')

  執行成功後,打開oplog表,可以看到oplog表中插入了一條記錄如圖(22)
    

                                                     圖(22)
  

  (2)、總結

        1、創建觸發器使用create trigger 觸發器名
        2、什麼時候觸發?after insert on users,除了after還有before,是在對錶操作之前(before)或者之後(after)觸發動作的。
        3、對什麼操作事件觸發? after insert on users,操作事件包括insert,update,delete等修改操作;
        4、對什麼表觸發? after insert on users
        5、影響的範圍?for each row
 

3、示例2


    需求:出於審計目的,當刪除users表時,記錄刪除前該記錄的主要字段值
    

create trigger tr_users_delete before delete on users
    for each row 
    begin 
        insert into oplog(userid,username,action,optime)
        values(OLD.id,OLD.name,'delete',now());
    end;


    刪除users表中的一條記錄
    

delete from users where id=6;

    執行成功後,打開oplog表,可以看到oplog表中插入了一條記錄如圖(23)

                                              圖(23)


         
十二、流程控制

 1、case分支

   (1)、基本語法結構

case ...
when ... then....
when.... then....
else ... 
end case;

(2)、示例

users表中,根據userid獲取status值,如果status爲1,則修改score爲10;如果status爲2,則修改爲20,如果status3,則修改爲30;否則修改爲40。
  

 create procedure testcate(userid int)
    begin 
        declare my_status int default 0;
        select status into my_status from users where id=userid;
 
        case my_status
            when 1 then update users set score=10 where id=userid;
            when 2 then update users set score=20 where id=userid;
            when 3 then update users set score=30 where id=userid;
            else update users set score=40 where id=userid;
        end case;
    end;


    調用過程 call testcate(1); ,執行結果如圖(24);

                             圖(24)

 

十四、存儲過程+event(事件)

 

     1、使用存儲過程+事件事件一個簡單的實現福彩3D開獎
        
        需求:設計一個福彩的開獎過程,沒3分鐘開獎一次
            第一步:先編寫一個存儲過程open_lottery,產生3個隨機數,生成一條開獎記錄
            第二步:編寫一個時間調度器,每3分鐘調用一次這個過程
          

create procedure open_lottery()
        begin 
            insert into lottery(num1,num2,num3,ctime)
            select FLOOR(rand()*9)+1,FLOOR(rand()*9)+1,FLOOR(rand()*9)+1,now();
        end;

 

create event if not exists lottery_event -- 創建一個事件
        on schedule every  3 minute  -- on schedule 什麼時候來執行,沒三分鐘執行一次
        on completion preserve 
        do call open_lottery;


        運行結果如圖(25)


        

                                                         圖(25)

注意,如果event之一沒有運行,請按照以下辦法解決:

(1)、 show variables like '%event_scheduler%';
        set global event_scheduler=on;

(2)、 alert event lottery_event enable;


    2、解析event的創建格式
    (1)、基本語法

create event[IF NOT EXISTS]event_name -- 創建使用create event
    ON SCHEDULE schedule -- on schedule 什麼時候來執行
    [ON COMPLETION [NOT] PRESERVE] -- 調度計劃執行完成後是否還保留
    [ENABLE | DISABLE] -- 是否開啓事件,默認開啓
    [COMMENT 'comment'] -- 事件的註釋
    DO sql_statement; -- 這個調度計劃要做什麼?

(2)、執行時間說明

    1.單次計劃任務示例
        在2019年2月1日4點執行一次

        on schedule at    '2019-02-01 04:00:00'
         
    2. 重複計劃執行
        on schedule every 1 second 每秒執行一次
        on schedule every 1 minute 每分鐘執行一次
        on schedule every 1 day 沒天執行一次
        
    3.指定時間範圍的重複計劃任務
        每天在20:00:00執行一次
        on schedule every 1 day starts '2019-02-01 20:00:00'
        

十五、本文所用到的表

 

1、lottery表

2、oplog表

3、orders表

4、test1表

5、user表

     

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章