MySQL5-函數/存儲過程與定時器、觸發器
阿新 • • 發佈:2017-06-06
名稱 狀態 訪問 safe 安全問題 ant comm gnu led 目錄
一、函數/存儲過程
二、定時器
三、觸發器
四、函數語句學習
:
一、函數/存儲過程
1、函數與存儲過程 (1) function與procedure的區別:一個有返回值,一個沒有,僅此而已。上述說法是錯誤的,function和procedure的用法有很多不同,總體來說procedure受到的限制較少,function的限制較多;而且procedure可以使用out參數返回值,因此盡量采用procedure。比如,以下存儲過程的創建是合法的,但函數的常見則是非法的。#存儲過程合法
DROP PROCEDURE IF EXISTS test;
CREATE PROCEDURE test()
BEGIN
DECLARE i INT;
SET i=1;
END;
#函數非法
DROP FUNCTION IF EXISTS test;
CREATE FUNCTION test()
BEGIN
DECLARE i INT;
SET i=1;
END;
(2)使用存儲過程/函數,比使用單獨的SQL語句要快。
2、創建函數
語法:
create procedure 存儲過程名字([in|out|inout] 參數 datatype)
comment ‘here is zhushi‘
begin
MySQL 語句;
end;
參數:如果不指定in/out/inout,則默認為in;()不能省;參數前不加@;參數不能指定默認值。參數中可以使用各種類型,包括varchar,但是一定要指定長度,否則報錯。
out參數drop procedure if exists bb;
create procedure bb(out total int)
begin
set total = 3;#方法1:不能有@,已驗證
#方法2已驗證可以:select count(*) into total from user;
end;
call bb(@total);
select @total;#調用語句和select語句中,都必須使用@,[email protected],[email protected]
返回值:
create function 存儲過程名字([in|out|inout] 參數 datatype) returns int
begin
MySQL 語句;
return 1;
end;
註釋:comment後面跟註釋,在show procedure status中可以看到;可以省略
函數體:如果MySQL語句不止一句,則需要begin/end;每條語句都需要分號;註釋可以使用/**/、--、#;函數中可以使用return,存儲過程中不能使用。
創建函數實例:
drop procedure if exists pr_add;
create procedure pr_add(a int,b int)
begin
declare c int;
if a is null then
set a = 0;
end if;
if b is null then
set b = 0;
end if;
set c = a + b;
select c as sum;
/* return c; 不能在 MySQL 存儲過程中使用。return 只能出現在函數中。*/
end;
3、delimiter:分界符;主要用在MySQL客戶端。
delimiter的作用是規定分界符;當客戶端遇到分界符時,便會執行MySQL命令。默認情況下,分界符是分號;但是在有些情景下,如定義函數,不希望遇到分號執行命令,而是等函數定義結束後執行命令,此時便使用形如delimiter $$的語句改變分界符,待函數定義結束後,再使用delimiter ;將分界符變回分號。
因此在非MySQL客戶端,如sql文件、Navicat for MySQL中執行語句時,不需要使用delimiter。
4、錯誤說明
Error Code: 1418. 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,slave會從master復制數據,而一些操作,比如function所得的結果在master和slave上可能不同,所以存在潛在的安全隱患。因此,在默認情況下回阻止function的創建。
解決方案1:將log_bin_trust_function_creators參數設置為ON,這樣一來開啟了log-bin的MySQL Server便可以隨意創建function。這裏存在潛在的數據安全問題,除非明確的知道創建的function在master和slave上的行為完全一致。
查看狀態:SHOW VARIABLES LIKE ‘%log_bin_trust_function_creators%‘
設置:set global log_bin_trust_function_creators=1【動態設置在重啟數據庫後失效;修改配置文件後重啟數據庫可以永久改變】
5、變量聲明:
declare用於在函數中聲明局部變量,只在函數中有效
DECLARE variable_name1,variable_name2,…… datatype(size) DEFAULT default_value;
set variable_name1 = 1;//賦值
SELECT COUNT(*) INTO total_products FROM products#另外一種賦值方式,值得註意
set @用於定義會話變量(嚴格說是用戶變量),在整個會話中都有效;不需要指定datatype,MySQL自動指定。
set @var = 1;
set用於對局部變量賦值時,必須等所有的declare語句執行結束(個人懷疑是不是在函數中,所有的declare必須都放在最前面)
#以下語句會報錯
drop procedure if exists test;
CREATE PROCEDURE test()
BEGIN
DECLARE i INT;
SET i = 1;
DECLARE o INT;
END;
#以下語句則正確
drop procedure if exists test;
CREATE PROCEDURE test()
BEGIN
DECLARE i INT;
DECLARE o INT;
SET i = 1;
END;
6、調用函數:不能省略參數,可以使用null代替
call pr_add(10,20);
或者
set @a = 10;
set @b = 20;
call pr_add(@a, @b);
7、刪除函數
drop procedure if exists aa;
drop precedure aa;#如果aa不存在則會錯誤
8、查看函數
show procedure/function status:查看該服務器上所有數據庫的所有procedure/function的狀態
show create procedure/function 名稱:查看指定函數的狀態
show procedure status like ‘%……%‘
二、定時器
1、確認版本:5.1以上開始支持
select version()
2、開啟event功能:默認是關閉的
show variables like ‘event_scheduler‘
set global event_scheduler=‘on‘;
3、創建event需要的存儲過程
drop procedure if exists test_proce;
create procedure test_proce()
begin
insert into `user`(name,age) values(‘zhoudan‘,‘18‘);
end;
4、創建定時器
drop event if exists test_event;
create event test_event
on schedule every 1 second #執行的間隔時間
starts ‘2015-01-10 00:10:00‘ #開始執行的時間
on completion preserve disable #創建定時器後是否立即啟動,如果是disable需要手動啟動
do call test_proce();
5、啟動、關閉、查看定時器
alter event test_event on completion preserve enable; #啟動定時器
alter event test_event on completion preserve disable; #關閉定時器
show events; #顯示本數據庫下所有定時器
6、註意:如果存儲過程執行過程中有錯誤,則整個過程中可能並不報錯,但是定時器指定的存儲過程無法正確執行。
三、觸發器
1、創建觸發器示例
create trigger newuser
after insert
on `user`
for each row
call test_proce1()
觸發器名:之前是表中唯一,現在不確定;穩妥的做法是數據庫唯一
執行時機:after/before
響應的活動:只支持insert/update/delete
關聯的表:on ‘user‘;目前MySQL只有表支持觸發器,視圖和臨時表都不支持
for each row:行級觸發器,即影響多少行就觸發多少次;與之對應的是語句觸發器,即語句執行一次出發一次;由於MySQL只支持行級觸發器,因此該語句不可省略。
執行的動作:
(1)可以是單條語句,可以是begin和end包圍的多條語句,也可以支持call()
(2)現在版本的動作不能返回值,如含有return語句的function,select語句等;如果包含select語句,會報錯Not allowed to return a result set from a trigger
(3)動作不能對本表進行 insert ,update ,delete 操作,以免遞歸循環觸發;不僅是insert不能觸發本表的insert語句,也不能觸發本表的update和delete語句,其他同理
2、一些限制
(1)每個表對每個活動只能有兩個觸發器(一前一後),因此一個表最多可以有6個觸發器
(2)一個觸發器不能與多個事件或多個表關聯
(3)如果before觸發器執行失敗,則MySQL不會執行相應操作和after觸發器;如果操作執行失敗,則MySQL觸發器不會執行after觸發器
3、刪除與查看:觸發器不能修改或覆蓋,如果要重新定義觸發器,必須先刪除在添加
drop trigger newuser;
show triggers;
show create trigger ‘newuser‘;
4、insert觸發器
說明:引用名字為new的虛擬表,訪問被插入的行;一般用before進行數據驗證、凈化,用after進行日誌處理等;對於auto_increment列,new在insert之前值為0,insert之後是新的自動生成的值
before:被插入的值可以被更改;註意語法(使用普通的update語句,創建觸發器的時候沒有問題,執行插入的時候就會報錯)
create trigger newuser2
before insert
on `user`
for each row
set new.name=‘zhoubapi‘
after
create trigger newuser
after insert
on `user`
for each row
insert into tmp values(new.id,new.name,new.age)
5、delete觸發器:引用名字為old的虛擬表訪問被刪除的行;old中的值全部只可讀
6、update觸發器:既可以引用old,也可以引用new;可讀性同上
四、函數語句學習
1、if
if a is null then
set a = 0;
end if;
if not exists(select 1 from ta_test where id = ‘1111‘) then
#mysql語句
end if;
2、while
declare i int;
set i=1;
while i<1000 do
#mysql語句
set i = i +1;
end while ;
3、錯誤處理【參考:http://blog.csdn.net/seteor/article/details/17791855】
(1)針對存儲過程 、觸發器或函數內部語句可能發生的錯誤或警告信息,需要進行相關異常的捕捉,然後作出相應的處理,異常處理的方式如下:
DECLARE {CONTINUE | EXIT} HANDLER FOR {SQLSTATE sqlstate_code| MySQL error code| condition_name} handler_actions
(2)Handler Type:CONTINUE | EXIT,處理類型,繼續或退出;exit只退出當前Block。
(3)Handler Condition:SQLSTATE sqlstate_code| MySQL error code| condition_name,觸發條件;其中condition_name(命名條件)既可以自己定義,也可是使用系統內置的SQLEXCEPTION、SQLWARNING和NOT FOUND。如果對於一個錯誤,同時有MySQL碼、SQLSTATE和命名條件可以捕獲,那麽只能捕獲一次錯誤,且捕獲順序是MySQL碼->SQLSTATE->命名條件。
(4)handler_actions:錯誤發生時執行的操作,在continue或exit之前執行。
(5)作用域:如果定義在begin/end之內,則begin/end之外的錯誤不會捕獲。
(6)示例:遊標與錯誤處理結合
DROP PROCEDURE IF EXISTS test;
CREATE PROCEDURE test()
BEGIN
DECLARE no_more_record INT DEFAULT 0;
DECLARE pname VARCHAR(100);
DECLARE cur_record CURSOR FOR SELECT name FROM ta_tmp WHERE id < 8;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_record = 1;
OPEN cur_record;
FETCH cur_record INTO pname;
WHILE no_more_record != 1 DO
SELECT pname;
FETCH cur_record INTO pname;
END WHILE;
CLOSE cur_record;
END;
4、臨時表【參考:http://blog.csdn.net/crazylaa/article/details/5368698】
(1)MySQL不支持數組,但是在存儲過程中,有時候需要使用復雜的運算結果,尤其是組合使用幾個表的數據時,此時可以考慮使用臨時表。
(2)臨時表:只有在當前連接情況下, TEMPORARY 表才是可見的。當連接關閉時, TEMPORARY 表被自動取消。這意味著兩個不同的連接可以使用相同的臨時表名稱,同時兩個臨時表不會互相沖突,也不與原有的同名的非臨時表沖突。
(3)基本的使用方法如下
#創建臨時表
CREATE TEMPORARY TABLE IF not exists tmpTable(
user_id VARCHAR(30) primary KEY,
drawingNumber int
);
truncate TABLE tmpTable;#在使用前清理數據
......#在後面的語句中可以對tmpTable進行增刪改查
MySQL5-函數/存儲過程與定時器、觸發器