mysql學習六:觸發器
觸發器
觸發器:trigger
,是指事先為某張表繫結一段程式碼,當表中的某些內容發生改變(增、刪、改)的時候,系統會自動觸發程式碼並執行。
觸發器包含三個要素,分別為
- 事件型別:增刪改,即
insert
、delete
和update
; - 觸發時間:事件型別前和後,即
before
和after
; - 觸發物件:表中的每一條記錄(行),即
整張表
。
每張表只能擁有一種觸發時間的一種事件型別的觸發器,即每張表最多可以擁有 6 種觸發器。
建立觸發器
-- 建立觸發器基本語法
delimiter 自定義符號 -- 臨時修改語句結束符,在後續語句中只有遇到自定義符號才會結束語句
create trigger + 觸發器名稱 + 觸發器時間 + 事件型別 on 表名 for each row
begin -- 代表觸發器內容開始
-- 觸發器內容主體,每行用分號結尾
end -- 代表觸發器內容結束
自定義符號 -- 用於結束語句
delimiter ; -- 恢復語句結束符
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
根據上述案例的需求,我們先來建立兩張表,分別為商品表goods
和訂單表orders
,SQL 語句如下:
-- 建立商品表
create table goods(
id int primary key auto_increment,
name varchar(20) not null,
price decimal(10, 2) default 0,
inventory int comment '商品庫存量'
)charset utf8;
-- 插入兩條資料
insert into goods values(null, 'iPhone8', 5088, 1000), (null, 'iPhoneX', 8088, 1000);
-- 建立訂單表
create table orders(
id int primary key auto_increment,
goods_id int not null,
goods_number int default 1
)charset utf8;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
接下來,執行如下 SQL 語句,建立觸發器:
-- 建立觸發器
delimiter $$ -- 臨時修改語句結束符
create trigger after_order after insert on orders for each row
begin -- 觸發器內容開始
-- 觸發器內容主體,每行用分號結尾
update goods set inventory = inventory - 1 where id = 1;
end -- 觸發器內容結束
$$ -- 結束語句
delimiter ; -- 恢復語句結束符
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
查詢觸發器
查詢所有觸發器或模糊匹配,
- 基本語法:
show triggers + [like 'pattern'];
執行如下 SQL 語句,進行測試:
-- 查詢所有觸發器,\G 表示旋轉
show triggers\G;
- 1
- 2
當然,我們也可以查詢建立觸發器的語句
- 基本語法:
show create trigger + 觸發器名稱;
執行如下 SQL 語句,進行測試:
-- 查詢觸發器建立語句,\G 表示旋轉
show create trigger after_order\G;
- 1
- 2
此外,所有的觸發器都會被系統保持到information_schema.triggers
這張表中,執行如下 SQL,進行測試:
-- 查詢觸發器,\G 表示旋轉
select * from information_schema.triggers\G;
- 1
- 2
使用觸發器
實際上,觸發器不是我們手動觸發的,而是在某種情況發生的時候自動觸發,例如我們上面建立的after_order
觸發器,當我們insert
訂單表的時候,該觸發器自動執行。執行如下 SQL 語句,進行測試:
-- 檢視商品表
select * from goods;
-- 檢視訂單表
select * from orders;
-- 插入訂單表
insert into orders values(null, 2, 10);
-- 檢視訂單表
select * from orders;
-- 檢視商品表
select * from goods;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
觀察上圖,我們會發現:觸發器確實生效了,在我們向orders
表insert
資料的時候,goods
表發生了變化;但是其並沒有如我們期望那樣執行,就算我們將goods_id
設定為2
,goods_number
設定為10
,觸發器操作的仍然是goods
表中id
為 1
的記錄且庫存量只減1
。且先不提這個問題,在建立觸發器的時候,我們要特別注意:觸發器的觸發物件和事件型別,決不能同觸發器主體的內容相同,防止發生死迴圈。
修改觸發器 & 刪除觸發器
觸發器不能修改,只能刪除。因此,當我們需要修改觸發器的時候,唯一的方法就是:先刪除,後新增。
- 基本語法:
drop trigger + 觸發器名稱;
執行如下 SQL 語句,進行測試:
-- 刪除觸發器
drop trigger after_order;
-- 查詢觸發器
show triggers;
- 1
- 2
- 3
- 4
觸發器記錄
觸發器記錄:無論觸發器是否觸發,只要當某種操作準備執行,系統就會將當前操作的記錄的當前狀態和即將執行之後的狀態分別記錄下來,供觸發器使用。其中,當前狀態被儲存到old
中,操作之後的狀態被儲存到new
中。至於old
和new
是什麼鬼?不知道大家是否還記得查看錶information_schema.triggers
的時候,標紅的兩個欄位:
ACTION_REFERENCE_OLD_ROW:OLD
ACTION_REFERENCE_NEW_ROW:NEW
其中,
OLD
,代表是舊記錄,也就是當前記錄的狀態,插入時沒有OLD
;NEW
,代表是新記錄,也就是假設操作發生之後記錄的狀態,刪除時沒有NEW
。
無論OLD
還是 NEW
,都代表記錄本身,而且任何一條記錄除了有資料,還有欄位名。因此,使用OLD
和 NEW
的方法就是:
- 基本語法:
OLD/NEW + . + 欄位名
在這裡,我們就能夠通過觸發器記錄解決剛才after_order
觸發器的問題了。依次執行如下 SQL 語句,進行測試:
-- 建立新觸發器
delimiter $$ -- 臨時修改語句結束符
create trigger after_order_new after insert on orders for each row
begin -- 觸發器內容開始
-- 觸發器內容主體,每行用分號結尾
update goods set inventory = inventory - NEW.goods_number where id = NEW.goods_id;
end -- 觸發器內容結束
$$ -- 結束語句
delimiter ; -- 恢復語句結束符
-- 檢視新觸發器
show triggers\G;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
-- 檢視商品表
select * from goods;
-- 檢視訂單表
select * from orders;
-- 插入訂單表
insert into orders values(null, 2, 10);
-- 檢視訂單表
select * from orders;
-- 檢視商品表
select * from goods;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如上圖所示,顯然after_order_new
觸發器按我們預期那樣正確的工作啦!