MySQL的自定義函數和存儲過程


1、MySQL的自定義函數(FUNCTION)

1.1、創建函數

MySQL 數據庫創建函數(Function)的語法:

CREATE FUNCTION func_name ( [func_parameter] )  -- 括號是必須的,參數是可選的
RETURNS type
[ characteristic ...] routine_body

說明如下:

  • CREATE FUNCTION:用來創建函數的關鍵字
  • func_name:函數名
  • func_parameters:函數的參數列表,參數列表的形式為:[IN | OUT | INOUT] param_name type。IN:表示輸入參數;OUT:表示輸出參數;INOUT:表示既可以輸入也可以輸出;param_name:表示參數的名稱;type:表示參數的類型,該類型可以是MySQL數據庫中的任意類型;
  • RETURNS type:函數返回數據的類型
  • characteristic:指定存儲函數的特性,取值與存儲過程時相同
  • routine_body:函數體。函數體由SQL代碼構成,可以是簡單SQL查詢語句或者是復合結構SQL語句。函數體若是復合結構(多行代碼)時,必須使用 begin...end 語句。復合結構可以包含聲明、流程控制,需結合使用 delimiter 來轉換(;)結束標識符。

函數體必須得有 return 語句,如果沒有就會報錯。return 語句可以不放在函數體的最后,但不建議這么做。函數體中如果只有一條語句,則可以不使用 begin...end 語句。

 

下面示例分別創建一個隨機生成字符串和隨機生成編號的函數,代碼如下:

-- 隨機產生字符串
drop function if exists rand_string;  -- 先判斷是否已存在同名函數,如果已存在則先刪除
DELIMITER $$ -- 兩個 $$ 表示結束
create function rand_string(n int) returns varchar(255)
begin
    declare chars_str varchar(100) default 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    declare return_str varchar(255) default '';
    declare i int default 0;
    while i < n do
        set return_str = concat(return_str, substring(chars_str, floor(1+rand()*52), 1));
        set i=i+1;
    end while;
    return return_str;
end $$
DELIMITER ;
-- 隨機生成編號
drop function if exists rand_num;
DELIMITER $$
create function rand_num() 
returns int(5)
begin
    declare i int default 0;
    set i=floor(100+rand()*10);
    return i;
end $$
DELIMITER ;

自定義函數的調用和其他普通函數的調用一樣,示例如下:

select rand_string(5);
select rand_num();

結果示例如下:

   

 

1.2、刪除函數

刪除函數的語句:

drop function function_name;

drop function [if exists] funcName;   -- 可以加個判斷,是否存在

-- 示例:
drop function if exists rand_num;

刪除函數的語法只需寫上函數名即可,函數的參數可以不用寫出來。

 

1.3、delimiter(設置分隔符的關鍵字)

delimiter 是分隔符的意思,在 mysql 中默認的分隔符是分號(即 ; )。默認情況下,mysql 在遇到分號 ; 時,則認為該語句已結束,在回車后,mysql 就會執行該條語句。但有時候,可能我們並不希望這樣。比如在創建自定義函數或者創建存儲過程時,我們可能會輸入多條語句,並且都帶有分號,但此時我們並不希望 mysql 立即執行這些語句。此時我們可以通過 delimiter 關鍵字來將分隔符臨時指定為其它符號,指定完后會在當前會話中有效。

語法為:

DELIMITER 加你想指定為分隔符的字符

-- 示例:
DELIMITER $$   -- 指定 $$ 為分隔符
DELIMITER //   -- 指定 // 為分隔符
DELIMITER ;    -- 指定 ; 為分隔符

 

比如,創建自定義函數:

DELIMITER $$   -- 先指定分隔符為 $$,其實可以指定為任意符號,比如 //、;;、@@等等
create function rand_num() 
returns int(5)
begin
    ...
end $$      -- 以指定的分隔符結束
DELIMITER ;   -- 重新指定分號為分隔符

上面就是,先將分隔符設置為 $$, 直到遇到下一個 $$,才整體執行語句。執行完后在最后一行 delimiter ; 又重新將 mysql 的分隔符設置為分號,如果不修改的話,本次會話中的所有分隔符都以 $$ 為准。

 

1.4、創建函數時報錯has none of DETERMINISTIC...

在創建函數時,MySQL可能會報以下錯誤:This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)

這是因為mysql 默認不允許創建自定義函數(安全性的考慮),此時我們需要將參數 log_bin_trust_function_creators 設置為開啟狀態。

可以通過以下命令查看 log_bin_trust_function_creators 參數:

show variables like 'log_bin_trust_function_creators';

如下則為關閉狀態:

 

執行以下命令將參數 log_bin_trust_function_creators 設置為開啟狀態:

set global log_bin_trust_function_creators=1;

再次查看時即可以看到為開啟狀態:

但這樣只是臨時設置,重啟終端后該設置即會失效。如果要配置永久的,需要在配置文件的 [mysqld] 上配置以下屬性:

log_bin_trust_function_creators=1

 

2、存儲過程(procedure)

存儲過程是一組可編程的函數,是為了完成特定功能的SQL語句集,經編譯創建並保存在數據庫中,用戶可通過指定存儲過程的名字並給定參數(需要時)來調用執行。

存儲過程就是具有名字的一段代碼,用來完成一個特定的功能。創建的存儲過程保存在數據庫的數據字典中。

使用存儲過程的好處:

  • 批量操作數據。存儲過程可以將一些重復性很高的操作,比如批量插入數據、批量刪除數據等,封裝到一個存儲過程中,簡化了對這些SQL的調用。
  • 批量處理:SQL+循環,減少流量,也就是“跑批”
  • 統一接口,確保數據的安全

 

2.1、創建存儲過程(create procedure)

創建語法:

create procedure 存儲過程名 ([params])
BEGIN
    存儲過程體(一組合法的SQL語句)
END

說明如下:

  • 參數列表(params):如果有多個參數則用逗號 , 分隔開,一個參數包括三部分:參數模式、參數名、參數類型,如:in name varchar(20)。參數模式有:in 輸入、out 輸出、inout 輸入輸出參數。
  • IN 參數:該參數作為輸入,必須在調用存儲過程時指定,在存儲過程中修改該參數的值不能被返回,為默認值。僅需要將數據傳入存儲過程,並不需要返回計算后的該值。只能當做傳入參數
  • OUT 參數:該參數作為輸出,該值可在存儲過程內部被改變,並可返回。不接受外部傳入的數據,僅返回計算之后的值。只能當做轉出參數。也就是說,即使傳值給 OUT 參數,該參數也無法得到你傳的值,得到的會是一個 null 值。
  • INOUT 參數:該參數即可作為輸入,又可做為輸出,也就是該參數既需要傳入值,又可以返回值。可當做傳入轉出參數

 

如果存儲過程體只有一條語句,則可以不用 begin...end。存儲過程體中的每條 SQL 語句的結尾要求必須寫分號。

 

示例如下:

-- 創建一個循環往dept表插入數據的存儲過程
drop procedure if exists insert_dept;
delimiter $$
create procedure insert_dept(in start int(10),in max_num int(10))   -- start為起始,max_num為插入的數量
begin
    declare i int default 0;
    set autocommit = 0;
    repeat
        set i = i+1;
        insert into dept(deptno,dname,loc) values((start+i),rand_string(10),rand_string(8));
    until i=max_num
    end repeat;
    commit;   -- 循環之后,一次性commit,避免多次連接數據庫。數據量太大的話可以改為多少條commit一次
end $$
DELIMITER ;

使用 call 關鍵字來調用存儲過程,如下:

CALL insert_dept(100, 5000);

 

2.2、刪除存儲過程(drop procedure)

刪除語法如下:

drop procedure [if exists] 存儲過程名;

-- 示例如下:
drop procedure my_insert;
drop procedure if exists my_insert;   -- 先判斷是否存在再刪除

 

2.3、存儲過程體的循環寫法

存儲過程體里面循環的寫法主要有以下:

-- 創建存儲過程
drop procedure if exists my_proc;
delimiter $$
create procedure my_proc()   
begin
     -- 第一種,while循環
     while i < 3 do 
            select i;
            set i = i +1;
    end while;

    -- 第二種,repeat循環
    REPEAT
            select j;
            set j = j +1;
    UNTIL j > 3 
    END REPEAT;


    -- 第三種,loop循環
    test_loop: LOOP
    select startDate;
        #開始時間加一天
        set startDate = DATE_FORMAT(date_add(startDate,interval 1 day),"%Y%m%d");
        IF startDate>endDate THEN
        LEAVE test_loop; 
    END IF; 
    END LOOP test_loop;
    

end $$
DELIMITER ;

 

如果有多個參數用","分割開


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM