#實驗七 函數與觸發器 第1關:定義、調用參數函數 相關知識 MySQL存儲函數 存儲函數和存儲過程一樣,都是在資料庫中定義一些 SQL 語句的集合。存儲函數可以通過 return 語句返回函數值,主要用於計算並返回一個值。而存儲過程沒有直接返回值,主要用於執行操作。 在 MySQL 中,使用 CR ...
實驗七 函數與觸發器
第1關:定義、調用參數函數
相關知識
MySQL存儲函數
存儲函數和存儲過程一樣,都是在資料庫中定義一些 SQL 語句的集合。存儲函數可以通過 return 語句返回函數值,主要用於計算並返回一個值。而存儲過程沒有直接返回值,主要用於執行操作。
在 MySQL 中,使用 CREATE FUNCTION 語句來創建存儲函數,其語法形式如下:
- CREATE FUNCTION sp_name ([func_parameter[...]])
- RETURNS type
- [characteristic ...] routine_body
其中:
- sp_name 參數:表示存儲函數的名稱;
- func_parameter:表示存儲函數的參數列表;
- RETURNS type:指定返回值的類型;
- characteristic 參數:指定存儲函數的特性,該參數的取值與存儲過程是一樣的;
- routine_body 參數:表示 SQL 代碼的內容,可以用 BEGIN...END 來標示 SQL 代碼的開始和結束。
func_parameter 可以由多個參數組成,其中每個參數由參數名稱和參數類型組成,其形式如下: [IN | OUT | INOUT] param_name type;
其中:
- IN 表示輸入參數,OUT 表示輸出參數,INOUT 表示既可以輸入也可以輸出;
- param_name 參數是存儲函數的參數名稱;
- type 參數指定存儲函數的參數類型,該類型可以是 MySQL 資料庫的任意數據類型。
例如:使用 CREATE FUNCTION 創建查詢 tb_student 表中某個學生姓名的函數,SQL 語句和執行過程如下:
- mysql> USE test;
- Database changed
- mysql> DELIMITER //
- mysql> CREATE FUNCTION func_student(id INT(11))
- -> RETURNS VARCHAR(20)
- -> COMMENT '查詢某個學生的姓名'
- ->BEGIN
- -> RETURN(SELECT name FROM tb_student WHERE tb_student.id = id);
- ->END//
- Query OK,0 rows affected (0.10 sec)
- mysql> DELIMITER ;
上述代碼中,創建了 func_student 函數,該函數擁有一個類型為 INT(11) 的參數 id,返回值為 VARCHAR(20) 類型。SELECT 語句從 tb_student 表中查詢 id 欄位值等於所傳入參數 id 值的記錄,同時返回該條記錄的 name 欄位值。
創建函數與創建存儲過程一樣,需要通過命令 DELIMITER //將 SQL 語句的結束符由";"修改為"//",最後通過命令 DELIMITER ;將結束符號修改成 SQL 語句中預設的結束符號。
編程要求
根據提示,在右側編輯器補充代碼。 定義、調用參數函數(func_sqty):查詢返回指定供應商的供應零件總數量。
測試說明
平臺會對你編寫的代碼進行測試:
測試輸入:無; 預期輸出:
- func_sqty('S1')
- 1100
- func_sqty('S2')
- 2000
- func_sqty('S3')
- 400
use demo;
#代碼開始
#定義、調用參數函數(func\_sqty):查詢返回指定供應商的供應零件總數量。
DELIMITER $$
CREATEFUNCTION func\_sqty(p\_sno CHAR(2))
RETURNS INT
BEGIN
DECLARE v\_sqty INT;
SELECTSUM(qty)INTO v\_sqty FROM spj WHERE sno = p\_sno;
RETURN v\_sqty;
END$$
DELIMITER ;
#代碼結束
select func\_sqty('S1');
select func\_sqty('S2');
select func\_sqty('S3');
第2關:觸發器應用(1)
相關知識
MySQL觸發器介紹
觸發器(trigger)是與表/庫上的操作事件相關的一類特殊的存儲過程,它在相關事件發生時被觸發執行,常用於實現數據的完整性約束和業務規則。
MySQL 的觸發器和存儲過程一樣,都是嵌入到 MySQL 中的一段程式,是 MySQL 中管理數據的有力工具。不同的是執行存儲過程要使用 CALL 語句來調用,而觸發器的執行不需要使用 CALL 語句來調用,也不需要手工啟動,而是通過對數據表的相關操作來觸發、激活從而實現執行。比如當對 student 表進行操作(INSERT,DELETE 或 UPDATE)時就會激活它執行。
觸發器的主要作用就是其能夠實現由主鍵和外鍵所不能保證的複雜參照完整性和數據的一致性約束,它能夠對資料庫中的表做級聯修改,提供比CHECK約束更複雜的的數據完整性約束,並自定義錯誤消息。觸發器的主要作用有以下幾個方面:
- 強制資料庫間的引用完整性
- 級聯修改資料庫中所有相關的表,自動觸發其它與之相關的操作
- 跟蹤變化,撤銷或回滾違法操作,防止非法修改數據
- 返回自定義的錯誤消息,約束無法返回信息,而觸發器可以
- 觸發器可以調用更多的存儲過程
MySQL創建觸發器
在 MySQL 5.7 中,可以使用 CREATE TRIGGER 語句創建觸發器。
語法格式如下:
- CREATE <觸發器名>< BEFORE | AFTER >
- <INSERT | UPDATE | DELETE >
- ON <表名> FOR EACH Row
- <觸發器主體>
語法說明如下。 (1) 觸發器名 觸發器的名稱,觸發器在當前資料庫中必須具有唯一的名稱。如果要在某個特定資料庫中創建,名稱前面應該加上資料庫的名稱。 (2) INSERT | UPDATE | DELETE觸發事件,用於指定激活觸發器的語句的種類。 註意:三種觸發器的執行時間如下。 INSERT:將新行插入表時激活觸發器。 DELETE: 從表中刪除某一行數據時激活觸發器。 UPDATE:更改表中某一行數據時激活觸發器。 (3) BEFORE | AFTER BEFORE 和 AFTER,觸發器被觸發的時刻,表示觸發器是在激活它的語句之前或之後觸發。若希望驗證新數據是否滿足條件,則使用 BEFORE 選項;若希望在激活觸發器的語句執行之後完成幾個或更多的改變,則通常使用 AFTER 選項。 (4) 表名 與觸發器相關聯的表名,此表必須是永久性表,不能將觸發器與臨時表或視圖關聯起來。 (5) 觸發器主體 觸發器動作主體,包含觸發器激活時將要執行的 MySQL 語句。如果要執行多個語句,可使用 BEGIN…END 複合語句結構。 在觸發器主體中,我們可以使用NEW表示將要插入的新行,OLD表示將要刪除的舊行。通過OLD,NEW獲取它們的欄位內容,方便在觸發操作中使用,下麵是對應事件是否支持OLD、NEW的對應關係:
事件 | OLD | NEW |
---|---|---|
INSERT | × | √ |
--- | --- | --- |
DELETE | √ | × |
UPDATE | √ | √ |
由於UPDATE相當於刪除舊行(OLD),然後插入新行(NEW),所以UPDATE同時支持OLD、NEW。 (6) FOR EACH ROW一般是指行級觸發,對於受觸發事件影響的每一行都要激活觸發器的動作。例如,使用 INSERT 語句向某個表中插入多行數據時,觸發器會對每一行數據的插入都執行相應的觸發器動作。
MySQL分隔符(DELIMITER)
MySQL預設使用";"作為分隔符,SQL語句遇到";"就會提交。而我們的觸發器中可能會有多個";"符,為了防止觸發器創建語句過早的提交,我們需要臨時修改MySQL分隔符,創建完後,再將分隔符改回來。使用DELIMITER可以修改分隔符,格式如下:
- DELIMITER $
- ...--觸發器創建語句;
- $ --提交創建語句;
- DELIMITER ;
MySQL刪除觸發器
- 可以使用drop trigger刪除觸發器: drop trigger trigger_name;
- 刪除前先判斷觸發器是否存在: drop trigger if exists trigger_name
MySQL查看觸發器
另外,在 MySQL 中,若需要查看資料庫中已有的觸發器,則可以使用 SHOW TRIGGERS 語句。 語法格式如下: SHOW TRIGGERS;
在 MySQL 中,所有觸發器的信息都存在 information_schema資料庫的 triggers表中,可以通過查詢命令 SELECT來查看,具體的語法如下: SELECT * FROM information_schema.triggers WHERE trigger_name= '觸發器名';
其中,'觸發器名'用來指定要查看的觸發器的名稱,需要用單引號引起來。這種方式可以查詢指定的觸發器,使用起來更加方便、靈活。
MySQL觸發器的使用
1** 、 MySQL 觸發器 Insert 觸發更新同一張表:** 下麵我們有一個表"tmp1",tmp1表有兩個整型欄位:n1、n2。我們要通過觸發器實現,在tmp1插入記錄時,自動將n2欄位的值設置為n1欄位的5倍。 創建測試表和觸發器:
- --創建測試表
- drop table if exists tmp1;
- create table tmp1 (n1 int, n2 int);
- --創建觸發器
- drop trigger if exists tmp1_insert;
- create trigger tmp1_insert
- before insert on tmp1
- for each row
- begin
- setnew.n2 =new.n1*5;
- end;
測試觸發更新效果:
- mysql> insert tmp1(n1) values(18);
- Query OK,1 row affected (0.01 sec)
- mysql> insert tmp1(n1) values(99);
- Query OK,1 row affected (0.00 sec)
- mysql>select*from tmp1;
- +------+------+
- | n1 | n2 |
- +------+------+
- |18|90|
- |99|495|
- +------+------+
- 2 rows inset(0.00 sec)
2、** MySQL 觸發器 Update **觸發更新另一張表: 下麵有有兩個表tmp1、tmp2,兩個表都有一個相同的欄位name。使用觸發器實現更新一個表的name時,將另外一個表的name也更新。 創建測試表和觸發器:
- --創建測試表和插入測試數據
- drop table if exists tmp1;
- drop table if exists tmp2;
- create table tmp1 (id int, name varchar(128))default charset='utf8';
- create table tmp2 (fid int, name varchar(128))default charset='utf8';
- insert into tmp1 values(1,'愛E族');
- insert into tmp2 values(1,'愛E族');
- --創建觸發器
- drop trigger if exists tmp1_update;
- create trigger tmp1_update
- after update on tmp1
- for each row
- begin
- update tmp2 set name=new.name where fid=new.id;
- end;
測試觸發更新效果:
- mysql>select*from tmp1;
- +------+---------+
- | id | name |
- +------+---------+
- |1|愛E族|
- +------+---------+
- 1 row inset(0.00 sec)
- mysql>select*from tmp2;
- +------+---------+
- | fid | name |
- +------+---------+
- |1|愛E族|
- +------+---------+
- 1 row inset(0.00 sec)
- mysql> update tmp1 set name='aiezu.com'where id=1;
- Query OK,1 row affected (0.00 sec)
- Rows matched:1Changed:1Warnings:0
- mysql>select*from tmp1;
- +------+-----------+
- | id | name |
- +------+-----------+
- |1| aiezu.com |
- +------+-----------+
- 1 row inset(0.00 sec)
- mysql>select*from tmp2;
- +------+-----------+
- | fid | name |
- +------+-----------+
- |1| aiezu.com |
- +------+-----------+
- 1 row inset(0.00 sec)
編程要求
定義一個觸發器(tr_spj_insert),完成向SPJ表新增數據時,及時更新所有供應商供應零件總數量。 根據提示,在右側編輯器補充代碼。
原始表結構及數據如下所示:
sno | sname | status | city | sqty |
---|---|---|---|---|
S1 | 精益 | 20 | 天津 | |
--- | --- | --- | --- | --- |
S2 | 盛錫 | 10 | 北京 | |
S3 | 東方紅 | 30 | 北京 | |
S4 | 豐泰盛 | 20 | 天津 | |
S5 | 為民 | 30 | 上海 | |
pno | pname | color | weight |
---|---|---|---|
P1 | 螺母 | 紅 | 12 |
--- | --- | --- | --- |
P2 | 螺栓 | 綠 | 17 |
P3 | 螺絲刀 | 藍 | 14 |
P4 | 螺絲刀 | 紅 | 14 |
P5 | 凸輪 | 藍 | 40 |
P6 | 齒輪 | 紅 | 30 |
jno | jname | city |
---|---|---|
J1 | 三建 | 北京 |
--- | --- | --- |
J2 | 一汽 | 長春 |
J3 | 彈簧廠 | 天津 |
J4 | 造船廠 | 天津 |
J5 | 機車廠 | 唐山 |
J6 | 無線電廠 | 常州 |
J7 | 半導體廠 | 南京 |
測試說明
平臺會對你編寫的代碼進行測試:
預期輸出:
- TRIGGER_NAME ACTION_TIMING EVENT_MANIPULATION EVENT_OBJECT_TABLE
- tr_spj_insert AFTER INSERT spj
- sno sname status city sqty
- S1 精益20天津1100
- S2 盛錫10北京2000
- S3 東方紅30北京400
- S4 豐泰盛20天津600
- S5 為民30上海1000
use demo;
#定義一個觸發器(tr\_spj\_insert),完成向SPJ表新增數據時,及時更新所有供應商供應零件總數量。
#代碼開始
DELIMITER $$
CREATETRIGGER tr\_spj\_insert AFTER INSERTON spj
FOREACHROW
BEGIN
UPDATE s SET sqty =(SELECTSUM(qty)FROM spj WHERE sno = NEW.sno)WHERE sno = NEW.sno;
END$$
DELIMITER ;
#代碼結束
#以下代碼不要改動或刪除,將會對創建的觸發器進行測試
insertinto spj values
('S1','P1','J1',200),
('S1','P1','J3',100),
('S1','P1','J4',700),
('S1','P2','J2',100),
('S2','P3','J1',400),
('S2','P3','J2',200),
('S2','P3','J4',500),
('S2','P3','J5',400),
('S2','P5','J1',400),
('S2','P5','J2',100),
('S3','P1','J1',200),
('S3','P3','J1',200),
('S4','P5','J1',100),
('S4','P6','J3',300),
('S4','P6','J4',200),
('S5','P2','J4',100),
('S5','P3','J1',200),
('S5','P6','J2',200),
('S5','P6','J4',500);
第3關:觸發器應用(2)
相關知識
MySQL觸發器介紹
觸發器(trigger)是與表/庫上的操作事件相關的一類特殊的存儲過程,它在相關事件發生時被觸發執行,常用於實現數據的完整性約束和業務規則。
MySQL 的觸發器和存儲過程一樣,都是嵌入到 MySQL 中的一段程式,是 MySQL 中管理數據的有力工具。不同的是執行存儲過程要使用 CALL 語句來調用,而觸發器的執行不需要使用 CALL 語句來調用,也不需要手工啟動,而是通過對數據表的相關操作來觸發、激活從而實現執行。比如當對 student 表進行操作(INSERT,DELETE 或 UPDATE)時就會激活它執行。
觸發器的主要作用就是其能夠實現由主鍵和外鍵所不能保證的複雜參照完整性和數據的一致性約束,它能夠對資料庫中的表做級聯修改,提供比CHECK約束更複雜的的數據完整性約束,並自定義錯誤消息。觸發器的主要作用有以下幾個方面:
- 強制資料庫間的引用完整性
- 級聯修改資料庫中所有相關的表,自動觸發其它與之相關的操作
- 跟蹤變化,撤銷或回滾違法操作,防止非法修改數據
- 返回自定義的錯誤消息,約束無法返回信息,而觸發器可以
- 觸發器可以調用更多的存儲過程
MySQL創建觸發器
在 MySQL 5.7 中,可以使用 CREATE TRIGGER 語句創建觸發器。
語法格式如下:
- CREATE <觸發器名>< BEFORE | AFTER >
- <INSERT | UPDATE | DELETE >
- ON <表名> FOR EACH Row
- <觸發器主體>
語法說明如下。 (1) 觸發器名 觸發器的名稱,觸發器在當前資料庫中必須具有唯一的名稱。如果要在某個特定資料庫中創建,名稱前面應該加上資料庫的名稱。 (2) INSERT | UPDATE | DELETE觸發事件,用於指定激活觸發器的語句的種類。 註意:三種觸發器的執行時間如下。 INSERT:將新行插入表時激活觸發器。 DELETE: 從表中刪除某一行數據時激活觸發器。 UPDATE:更改表中某一行數據時激活觸發器。 (3) BEFORE | AFTER BEFORE 和 AFTER,觸發器被觸發的時刻,表示觸發器是在激活它的語句之前或之後觸發。若希望驗證新數據是否滿足條件,則使用 BEFORE 選項;若希望在激活觸發器的語句執行之後完成幾個或更多的改變,則通常使用 AFTER 選項。 (4) 表名 與觸發器相關聯的表名,此表必須是永久性表,不能將觸發器與臨時表或視圖關聯起來。 (5) 觸發器主體 觸發器動作主體,包含觸發器激活時將要執行的 MySQL 語句。如果要執行多個語句,可使用 BEGIN…END 複合語句結構。 在觸發器主體中,我們可以使用NEW表示將要插入的新行,OLD表示將要刪除的舊行。通過OLD,NEW獲取它們的欄位內容,方便在觸發操作中使用,下麵是對應事件是否支持OLD、NEW的對應關係:
事件 | OLD | NEW |
---|---|---|
INSERT | × | √ |
--- | --- | --- |
DELETE | √ | × |
UPDATE | √ | √ |
由於UPDATE相當於刪除舊行(OLD),然後插入新行(NEW),所以UPDATE同時支持OLD、NEW。 (6) FOR EACH ROW一般是指行級觸發,對於受觸發事件影響的每一行都要激活觸發器的動作。例如,使用 INSERT 語句向某個表中插入多行數據時,觸發器會對每一行數據的插入都執行相應的觸發器動作。
MySQL分隔符(DELIMITER)
MySQL預設使用";"作為分隔符,SQL語句遇到";"就會提交。而我們的觸發器中可能會有多個";"符,為了防止觸發器創建語句過早的提交,我們需要臨時修改MySQL分隔符,創建完後,再將分隔符改回來。使用DELIMITER可以修改分隔符,格式如下:
- DELIMITER $
- ...--觸發器創建語句;
- $ --提交創建語句;
- DELIMITER ;
MySQL刪除觸發器
- 可以使用drop trigger刪除觸發器: drop trigger trigger_name;
- 刪除前先判斷觸發器是否存在: drop trigger if exists trigger_name
MySQL查看觸發器
另外,在 MySQL 中,若需要查看資料庫中已有的觸發器,則可以使用 SHOW TRIGGERS 語句。 語法格式如下: SHOW TRIGGERS;
在 MySQL 中,所有觸發器的信息都存在 information_schema資料庫的 triggers表中,可以通過查詢命令 SELECT來查看,具體的語法如下: SELECT * FROM information_schema.triggers WHERE trigger_name= '觸發器名';
其中,'觸發器名'用來指定要查看的觸發器的名稱,需要用單引號引起來。這種方式可以查詢指定的觸發器,使用起來更加方便、靈活。
MySQL觸發器的使用
1** 、 MySQL 觸發器 Insert 觸發更新同一張表:** 下麵我們有一個表"tmp1",tmp1表有兩個整型欄位:n1、n2。我們要通過觸發器實現,在tmp1插入記錄時,自動將n2欄位的值設置為n1欄位的5倍。 創建測試表和觸發器:
- --創建測試表
- drop table if exists tmp1;
- create table tmp1 (n1 int, n2 int);
- --創建觸發器
- drop trigger if exists tmp1_insert;
- create trigger tmp1_insert
- before insert on tmp1
- for each row
- begin
- setnew.n2 =new.n1*5;
- end;
測試觸發更新效果:
- mysql> insert tmp1(n1) values(18);
- Query OK,1 row affected (0.01 sec)
- mysql> insert tmp1(n1) values(99);
- Query OK,1 row affected (0.00 sec)
- mysql>select*from tmp1;
- +------+------+
- | n1 | n2 |
- +------+------+
- |18|90|
- |99|495|
- +------+------+
- 2 rows inset(0.00 sec)
2、** MySQL 觸發器 Update **觸發更新另一張表: 下麵有有兩個表tmp1、tmp2,兩個表都有一個相同的欄位name。使用觸發器實現更新一個表的name時,將另外一個表的name也更新。 創建測試表和觸發器:
- --創建測試表和插入測試數據
- drop table if exists tmp1;
- drop table if exists tmp2;
- create table tmp1 (id int, name varchar(128))default charset='utf8';
- create table tmp2 (fid int, name varchar(128))default charset='utf8';
- insert into tmp1 values(1,'愛E族');
- insert into tmp2 values(1,'愛E族');
- --創建觸發器
- drop trigger if exists tmp1_update;
- create trigger tmp1_update
- after update on tmp1
- for each row
- begin
- update tmp2 set name=new.name where fid=new.id;
- end;
測試觸發更新效果:
- mysql>select*from tmp1;
- +------+---------+
- | id | name |
- +------+---------+
- |1|愛E族|
- +------+---------+
- 1 row inset(0.00 sec)
- mysql>select*from tmp2;
- +------+---------+
- | fid | name |
- +------+---------+
- |1|愛E族|
- +------+---------+
- 1 row inset(0.00 sec)
- mysql> update tmp1 set name='aiezu.com'where id=1;
- Query OK,1 row affected (0.00 sec)
- Rows matched:1Changed:1Warnings:0
- mysql>select*from tmp1;
- +------+-----------+
- | id | name |
- +------+-----------+
- |1| aiezu.com |
- +------+-----------+
- 1 row inset(0.00 sec)
- mysql>select*from tmp2;
- +------+-----------+
- | fid | name |
- +------+-----------+
- |1| aiezu.com |
- +------+-----------+
- 1 row inset(0.00 sec)
編程要求
定義一個觸發器(tr_spj_delete),完成向SPJ表刪除數據時,及時更新所有供應商供應零件總數量。 根據提示,在右側編輯器補充代碼。
原始表結構及數據如下所示:
sno | sname | status | city | sqty |
---|---|---|---|---|
S1 | 精益 | 20 | 天津 | |
--- | --- | --- | --- | --- |
S2 | 盛錫 | 10 | 北京 | |
S3 | 東方紅 | 30 | 北京 | |
S4 | 豐泰盛 | 20 | 天津 | |
S5 | 為民 | 30 | 上海 | |
pno | pname | color | weight |
---|---|---|---|
P1 | 螺母 | 紅 | 12 |
--- | --- | --- | --- |
P2 | 螺栓 | 綠 | 17 |
P3 | 螺絲刀 | 藍 | 14 |
P4 | 螺絲刀 | 紅 | 14 |
P5 | 凸輪 | 藍 | 40 |
P6 | 齒輪 | 紅 | 30 |
jno | jname | city |
---|---|---|
J1 | 三建 | 北京 |
--- | --- | --- |
J2 | 一汽 | 長春 |
J3 | 彈簧廠 | 天津 |
J4 | 造船廠 | 天津 |
J5 | 機車廠 | 唐山 |
J6 | 無線電廠 | 常州 |
J7 | 半導體廠 | 南京 |
sno | pno | jno | qty |
---|---|---|---|
S1 | P1 | J1 | 200 |
--- | --- | --- | --- |
S1 | P1 | J3 | 100 |
S1 | P1 | J4 | 700 |
S1 | P2 | J2 | 100 |
S2 | P3 | J1 | 400 |
S2 | P3 | J2 | 200 |
S2 | P3 | J4 | 500 |
S2 | P3 | J5 | 400 |
S2 | P5 | J1 | 400 |
S2 | P5 | J2 | 100 |
S3 | P1 | J1 | 200 |
S3 | P3 | J1 | 200 |
S4 | P5 | J1 | 100 |
S4 | P6 | J3 | 300 |
S4 | P6 | J4 | 200 |
S5 | P2 | J4 | 100 |
S5 | P3 | J1 | 200 |
S5 | P6 | J2 | 200 |
S5 | P6 | J4 | 500 |
測試說明
平臺會對你編寫的代碼進行測試:
預期輸出:
- TRIGGER_NAME ACTION_TIMING EVENT_MANIPULATION EVENT_OBJECT_TABLE
- tr_spj_delete AFTER DELETE spj
- sno sname status city sqty
- S1 精益20天津900
use demo;
#定義一個觸發器(tr\_spj\_delete),完成向SPJ表刪除數據時,及時更新所有供應商供應零件總數量。
#代碼開始
DELIMITER $$
CREATETRIGGER tr\_spj\_delete AFTER DELETEON spj FOREACHROW
BEGIN
UPDATE s SET sqty = sqty - OLD.qty WHERE sno = OLD.sno;
END$$
DELIMITER ;
UPDATE s
JOIN(
SELECT sno,SUM(qty)AS total\_qty
FROM spj
GROUPBY sno
)AS t ON s.sno = t.sno
SET s.sqty = t.total\_qty;
#代碼結束
#以下代碼不要改動或刪除,將會對創建的觸發器進行測試
DELETEFROM spj WHERE sno='S1'AND pno='P1'AND jno='J1';
第4關:觸發器應用(3)
相關知識
MySQL觸發器介紹
觸發器(trigger)是與表/庫上的操作事件相關的一類特殊的存儲過程,它在相關事件發生時被觸發執行,常用於實現數據的完整性約束和業務規則。
MySQL 的觸發器和存儲過程一樣,都是嵌入到 MySQL 中的一段程式,是 MySQL 中管理數據的有力工具。不同的是執行存儲過程要使用 CALL 語句來調用,而觸發器的執行不需要使用 CALL 語句來調用,也不需要手工啟動,而是通過對數據表的相關操作來觸發、激活從而實現執行。比如當對 student 表進行操作(INSERT,DELETE 或 UPDATE)時就會激活它執行。
觸發器的主要作用就是其能夠實現由主鍵和外鍵所不能保證的複雜參照完整性和數據的一致性約束,它能夠對資料庫中的表做級聯修改,提供比CHECK約束更複雜的的數據完整性約束,並自定義錯誤消息。觸發器的主要作用有以下幾個方面:
- 強制資料庫間的引用完整性
- 級聯修改資料庫中所有相關的表,自動觸發其它與之相關的操作
- 跟蹤變化,撤銷或回滾違法操作,防止非法修改數據
- 返回自定義的錯誤消息,約束無法返回信息,而觸發器可以
- 觸發器可以調用更多的存儲過程
MySQL創建觸發器
在 MySQL 5.7 中,可以使用 CREATE TRIGGER 語句創建觸發器。
語法格式如下:
- CREATE <觸發器名>< BEFORE | AFTER >
- <INSERT | UPDATE | DELETE >
- ON <表名> FOR EACH Row
- <觸發器主體>
語法說明如下。 (1) 觸發器名 觸發器的名稱,觸發器在當前資料庫中必須具有唯一的名稱。如果要在某個特定資料庫中創建,名稱前面應該加上資料庫的名稱。 (2) INSERT | UPDATE | DELETE觸發事件,用於指定激活觸發器的語句的種類。 註意:三種觸發器的執行時間如下。 INSERT:將新行插入表時激活觸發器。 DELETE: 從表中刪除某一行數據時激活觸發器。 UPDATE:更改表中某一行數據時激活觸發器。 (3) BEFORE | AFTER BEFORE 和 AFTER,觸發器被觸發的時刻,表示觸發器是在激活它的語句之前或之後觸發。若希望驗證新數據是否滿足條件,則使用 BEFORE 選項;若希望在激活觸發器的語句執行之後完成幾個或更多的改變,則通常使用 AFTER 選項。 (4) 表名 與觸發器相關聯的表名,此表必須是永久性表,不能將觸發器與臨時表或視圖關聯起來。 (5) 觸發器主體 觸發器動作主體,包含觸發器激活時將要執行的 MySQL 語句。如果要執行多個語句,可使用 BEGIN…END 複合語句結構。 在觸發器主體中,我們可以使用NEW表示將要插入的新行,OLD表示將要刪除的舊行。通過OLD,NEW獲取它們的欄位內容,方便在觸發操作中使用,下麵是對應事件是否支持OLD、NEW的對應關係:
事件 | OLD | NEW |
---|---|---|
INSERT | × | √ |
--- | --- | --- |
DELETE | √ | × |
UPDATE | √ | √ |
由於UPDATE相當於刪除舊行(OLD),然後插入新行(NEW),所以UPDATE同時支持OLD、NEW。 (6) FOR EACH ROW一般是指行級觸發,對於受觸發事件影響的每一行都要激活觸發器的動作。例如,使用 INSERT 語句向某個表中插入多行數據時,觸發器會對每一行數據的插入都執行相應的觸發器動作。
MySQL分隔符(DELIMITER)
MySQL預設使用";"作為分隔符,SQL語句遇到";"就會提交。而我們的觸發器中可能會有多個";"符,為了防止觸發器創建語句過早的提交,我們需要臨時修改MySQL分隔符,創建完後,再將分隔符改回來。使用DELIMITER可以修改分隔符,格式如下:
- DELIMITER $
- ...--觸發器創建語句;
- $ --提交創建語句;
- DELIMITER ;
MySQL刪除觸發器
- 可以使用drop trigger刪除觸發器: drop trigger trigger_name;
- 刪除前先判斷觸發器是否存在: drop trigger if exists trigger_name
MySQL查看觸發器
另外,在 MySQL 中,若需要查看資料庫中已有的觸發器,則可以使用 SHOW TRIGGERS 語句。 語法格式如下: SHOW TRIGGERS;
在 MySQL 中,所有觸發器的信息都存在 information_schema資料庫的 triggers表中,可以通過查詢命令 SELECT來查看,具體的語法如下: SELECT * FROM information_schema.triggers WHERE trigger_name= '觸發器名';
其中,'觸發器名'用來指定要查看的觸發器的名稱,需要用單引號引起來。這種方式可以查詢指定的觸發器,使用起來更加方便、靈活。
MySQL觸發器的使用
1** 、 MySQL 觸發器 Insert 觸發更新同一張表:** 下麵我們有一個表"tmp1",tmp1表有兩個整型欄位:n1、n2。我們要通過觸發器實現,在tmp1插入記錄時,自動將n2欄位的值設置為n1欄位的5倍。 創建測試表和觸發器:
- --創建測試表
- drop table if exists tmp1;
- create table tmp1 (n1 int, n2 int);
- --創建觸發器
- drop trigger if exists tmp1_insert;
- create trigger tmp1_insert
- before insert on tmp1
- for each row
- begin
- setnew.n2 =new.n1*5;
- end;
測試觸發更新效果:
- mysql> insert tmp1(n1) values(18);
- Query OK,1 row affected (0.01 sec)
- mysql> insert tmp1(n1) values(99);
- Query OK,1 row affected (0.00 sec)
- mysql>select*from tmp1;
- +------+------+
- | n1 | n2 |
- +------+------+
- |18|90|
- |99|495|
- +------+------+
- 2 rows inset(0.00 sec)
2、** MySQL 觸發器 Update **觸發更新另一張表: 下麵有有兩個表tmp1、tmp2,兩個表都有一個相同的欄位name。使用觸發器實現更新一個表的name時,將另外一個表的name也更新。 創建測試表和觸發器:
- --創建測試表和插入測試數據
- drop table if exists tmp1;
- drop table if exists tmp2;
- create table tmp1 (id int, name varchar(128))default charset='utf8';
- create table tmp2 (fid int, name varchar(128))default charset='utf8';
- insert into tmp1 values(1,'愛E族');
- insert into tmp2 values(1,'愛E族');
- --創建觸發器
- drop trigger if exists tmp1_update;
- create trigger tmp1_update
- after update on tmp1
- for each row
- begin
- update tmp2 set name=new.name where fid=new.id;
- end;
測試觸發更新效果:
- mysql>select*from tmp1;
- +------+---------+
- | id | name |
- +------+---------+
- |1|愛E族|
- +------+---------+
- 1 row inset(0.00 sec)
- mysql>select*from tmp2;
- +------+---------+
- | fid | name |
- +------+---------+
- |1|愛E族|
- +------+---------+
- 1 row inset(0.00 sec)
- mysql> update tmp1 set name='aiezu.com'where id=1;
- Query OK,1 row affected (0.00 sec)
- Rows matched:1Changed:1Warnings:0
- mysql>select*from tmp1;
- +------+-----------+
- | id | name |
- +------+-----------+
- |1| aiezu.com |
- +------+-----------+
- 1 row inset(0.00 sec)
- mysql>select*from tmp2;
- +------+-----------+
- | fid | name |
- +------+-----------+
- |1| aiezu.com |
- +------+-----------+
- 1 row inset(0.00 sec)
編程要求
定義一個觸發器(tr_spj_delete),完成向SPJ表刪除數據時,及時更新所有供應商供應零件總數量。 根據提示,在右側編輯器補充代碼。
原始表結構及數據如下所示:
sno | sname | status | city | sqty |
---|---|---|---|---|
S1 | 精益 | 20 | 天津 | |
--- | --- | --- | --- | --- |
S2 | 盛錫 | 10 | 北京 | |
S3 | 東方紅 | 30 | 北京 | |
S4 | 豐泰盛 | 20 | 天津 | |
S5 | 為民 | 30 | 上海 | |
pno | pname | color | weight |
---|---|---|---|
P1 | 螺母 | 紅 | 12 |
--- | --- | --- | --- |
P2 | 螺栓 | 綠 | 17 |
P3 | 螺絲刀 | 藍 | 14 |
P4 | 螺絲刀 | 紅 | 14 |
P5 | 凸輪 | 藍 | 40 |
P6 | 齒輪 | 紅 | 30 |
jno | jname | city |
---|---|---|
J1 | 三建 | 北京 |
--- | --- | --- |
J2 | 一汽 | 長春 |
J3 | 彈簧廠 | 天津 |
J4 | 造船廠 | 天津 |
J5 | 機車廠 | 唐山 |
J6 | 無線電廠 | 常州 |
J7 | 半導體廠 | 南京 |
sno | pno | jno | qty |
---|---|---|---|
S1 | P1 | J1 | 200 |
--- | --- | --- | --- |
S1 | P1 | J3 | 100 |
S1 | P1 | J4 | 700 |
S1 | P2 | J2 | 100 |
S2 | P3 | J1 | 400 |
S2 | P3 | J2 | 200 |
S2 | P3 | J4 | 500 |
S2 | P3 | J5 | 400 |
S2 | P5 | J1 | 400 |
S2 | P5 | J2 | 100 |
S3 | P1 | J1 | 200 |
S3 | P3 | J1 | 200 |
S4 | P5 | J1 | 100 |
S4 | P6 | J3 | 300 |
S4 | P6 | J4 | 200 |
S5 | P2 | J4 | 100 |
S5 | P3 | J1 | 200 |
S5 | P6 | J2 | 200 |
S5 | P6 | J4 | 500 |
測試說明
平臺會對你編寫的代碼進行測試:
預期輸出:
- TRIGGER_NAME ACTION_TIMING EVENT_MANIPULATION EVENT_OBJECT_TABLE
- tr_spj_update AFTER UPDATE spj
- sno sname status city sqty
- S1 精益20天津1500
- S2 盛錫10北京1600
use demo;
#定義一個觸發器(tr\_spj\_update),完成向SPJ表更新數據時,及時更新所有供應商供應零件總數量。
#代碼開始
DELIMITER $$
CREATETRIGGER tr\_spj\_update AFTER UPDATEON spj FOREACHROW
BEGIN
-- 如果供應商發生變化,更新舊供應商和新供應商的數量
IF OLD.sno \<\> NEW.sno THEN
UPDATE s SET sqty = sqty - OLD.qty WHERE sno = OLD.sno;
UPDATE s SET sqty = sqty + NEW.qty WHERE sno = NEW.sno;
-- 否則,只更新數量變化的供應商
ELSEIF OLD.qty \<\> NEW.qty THEN
UPDATE s SET sqty = sqty +(NEW.qty - OLD.qty)WHERE sno = NEW.sno;
END IF;
END$$
DELIMITER ;
UPDATE s
JOIN(
SELECT sno,SUM(qty)AS total\_qty
FROM spj
GROUPBY sno
)AS t ON s.sno = t.sno
SET s.sqty = t.total\_qty;
#代碼結束
#以下代碼不要改動或刪除,將會對創建的觸發器進行測試
UPDATE spj SET sno='S1'WHERE sno='S2'AND pno='P3'AND jno='J1';