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 ;
如果有多個參數用","分割開