實作 : 練習SELECT、INSERT INTO、UPDATE、DELETE FROM以及Trigger的使用

這個實作要在XAMPP內,利用觸發器(Trigger)來監督表單的插入(Insert into)、更新(Update)、刪除(Delete From),監督的意思是要知道誰在何時對資料表做了哪些更動資料的動作。例如哪個帳號在何時插入資料? 哪個帳號在何時將資料欄位更新? 哪個帳號在何時刪除資料?

要達成這樣的目的,有兩種方式 : 在寫程式的時候,將更動資料的動作寫入Log檔案,或是在資料庫中使用觸發器。前者是program-based,後者是SQL-based。

哪種方式比較好? 當然是SQL-based,因為如果使用program-based,只有透過程式介面去更動資料才會寫入Log檔案,如果從後台資料庫直接更動資料,program-based就無法監督。

以下就是以SQL-based的方式,只要資料表有被更動,觸發器就會依照規定的方式開始動作。

以PhpMyAdmin介面來實作

步驟一 : 先啟動Apache與MySQL後,點選Admin以進入PhpMyAdmin資料管理介面。

如下圖 :


步驟二 : 進入 PhpMyAdmin資料管理介面後,點選新增來建立一個資料庫。

如下圖 :


名稱填入 School,資料表的[字元集排序原則]選擇utf8_general_ci

utf8_general_ci 意思是使用字元集排序原則是 utf8 general,並且不分大小寫。其他例如utf8mb4_unicode_ci、utf8_general_cs、utf8mb4_unicode_cs等。

參考資料 :
https://www.mysql.tw/2013/03/mysql-character-set-collation.html
https://www.mysql.tw/2013/03/blog-post.html

如下圖 :


按下建立之後,就會增加一個資料庫 school。

如下圖 :


步驟三 : 再來就點選 school,然後點選SQL,這樣就能開始針對school資料庫來寫SQL語法。

如下圖 :


先以下列語法建立 students 資料表單,然後按下執行。

CREATE TABLE students (
stud_id INT PRIMARY KEY,
stud_name VARCHAR(50) NOT NULL
);

如下圖 :


再以下列語法建立audit_log資料表單,然後按下執行。

CREATE TABLE audit_log (
log_id BIGINT PRIMARY KEY AUTO_INCREMENT,
act_what VARCHAR(50),
act_who VARCHAR(50),
act_when DATETIME
);

如下圖 :


步驟四 : 接著就來建立Trigger。 

以下列語法建立students_after_insert觸發器,然後按下執行。

DELIMITER //
CREATE TRIGGER students_after_insert
AFTER INSERT
ON students
FOR EACH ROW
Insert into audit_log(act_what, act_who, act_when) VALUES (concat('A  ', convert(NEW.stud_id,char)), (select user()), now()) //
DELIMITER ;

上面語句的意思,透過delimiter更改結尾方式,由原本的 ; 改為 //

因為語法中如果有 ; 的話,整個語句就開始執行,但我們希望整個邏輯寫完再送出去執行。

其他的語法,其實蠻固定的 : 

DELIMITER //
CREATE TRIGGER [trigger name]
AFTER INSERT
ON [table name]
FOR EACH ROW
[執行的敘述] //
DELIMITER ;

如果執行的敘述是多行的話,會變成 :

DELIMITER //
CREATE TRIGGER [trigger name] 
AFTER INSERT 
ON [table name] 
FOR EACH ROW
BEGIN
[執行的敘述#1] ;
[執行的敘述#2] ;
END //
DELIMTER;

上面的 AFTER INSERT 也可以是 BEFORE INSERT、AFTER DELETE、BEFORE DELETE、AFTER UPDATE、BEFORE UPDATE。

上面的select user(),就是指現在執行的帳號,now()就是現在的時間,而NEW.stud_id就是指新的stud_id。

如下圖 :


以下列語法建立students_after_update觸發器,然後按下執行。

DELIMITER //
CREATE TRIGGER students_after_update
AFTER UPDATE
ON students
FOR EACH ROW
Insert into audit_log(act_what, act_who, act_when) values (concat('U ',convert(old.stud_id,char)),(SELECT USER()),NOW()) //
DELIMITER ;

當然有NEW.stud_id也就會有OLD.stud_id,就是更新前的stud_id,convert跟contact都是MySQL的既有函式。

如下圖 :


以下列語法建立students_after_delete觸發器,然後按下執行。

DELIMITER //
CREATE TRIGGER students_after_delete
AFTER DELETE
ON students
FOR EACH ROW
Insert into audit_log(act_what, act_who, act_when) values (concat('D ',convert(old.stud_id,char)),(SELECT USER()),NOW());//
DELIMITER ;

如下圖 :



步驟五 : 以插入、更新、刪除等語法來看看監看的結果。

以下列語法進行插入、更新、刪除 :

insert into students values (6, 'superman');
update students set stud_name='super' where stud_id=6;
delete from students where stud_id=6;

(不過上述的6只是範例資料,你自己要選一個不重複的stud_id值)

如下圖 :


步驟六 : 看看audit_log紀錄了什麼資料。

如下圖 :


以上表單,看到root@localhost這個帳號,在該時間做了A 6 (也就是插入一筆stud_id為6的資料),然後又在另外時間做了U 6 (也就是更新stud_id為6的資料),然後又在另外時間做了D 6 (也就是刪除stud_id為6的資料)。

只是由這個方式,並不知道更新或是刪除了什麼。

以指令方式來實作

步驟一 :  先啟動Apache與MySQL後,點選Shell進入指令模式。

如下圖 : 



輸入以下指令登入 (如果root密碼空白)

mysql -u root

如下圖 : 


輸入以下指令登入 (如果root密碼不是空白)

mysql -u root -p

如下圖 : 


步驟二 : 建立一個新的資料庫。

CREATE DATABASE school
CHARACTER SET utf8
COLLATE utf8_general_ci;

如下圖 : 


步驟三 : 選擇 school 資料庫並建立資料表。

USE school;

CREATE TABLE students (
stud_id INT PRIMARY KEY,
stud_name VARCHAR(50) NOT NULL
);

CREATE TABLE audit_log (
log_id BIGINT PRIMARY KEY AUTO_INCREMENT,
act_what VARCHAR(50),
act_who VARCHAR(50),
act_when DATETIME
);

如下圖 : 


步驟四 : 接著就來建立Trigger。 

以下列語法建立students_after_insert觸發器。

DELIMITER //
CREATE TRIGGER students_after_insert
AFTER INSERT
ON students
FOR EACH ROW
Insert into audit_log(act_what, act_who, act_when) VALUES (concat('ADD  ', convert(NEW.stud_id,char)), (select user()), now()) //
DELIMITER ;

以下列語法建立students_after_update觸發器。

DELIMITER //
CREATE TRIGGER students_after_update
AFTER UPDATE
ON students
FOR EACH ROW
Insert into audit_log(act_what, act_who, act_when) values (concat('U ',convert(old.stud_id,char)),(SELECT USER()),NOW()) //
DELIMITER ;

以下列語法建立students_after_delete觸發器。

DELIMITER //
CREATE TRIGGER students_after_delete
AFTER DELETE
ON students
FOR EACH ROW
Insert into audit_log(act_what, act_who, act_when) values (concat('D ',convert(old.stud_id,char)),(SELECT USER()),NOW());//
DELIMITER ;

如下圖 : 



步驟五 : 以插入、更新、刪除等語法來看看監看的結果。

以下列語法進行插入、更新、刪除 :

insert into students values (6, 'superman');
update students set stud_name='super' where stud_id=6;
delete from students where stud_id=6;

如下圖 : 


步驟六 : 看看audit_log紀錄了什麼資料。

SELECT * FROM audit_log;


[思考]

這個只是初步的監督結果,只知道更新了資料,但是不知道更新前是什麼資料? 只知道刪除了資料,但是不知道刪除的資料是什麼? 如果我們希望監督的觸發器要知道stud_name被更新前是什麼資料? 要知道stud_name被刪除前是什麼資料? 應該怎麼做?

~~~~~~~~~~~~~~~~~~~~~~~~~

我們還是使用school這個資料庫,把觸發器用在另外一個資料表 teachers (tid, tname),並且監看的log資料表 audit_teacher (aid, act_what, act_who, act_when, aname)

以上 :

tid : teacher id
tname : teacher name
aid : audit id
act_what : 做了什麼動作
act_who : 誰做了
act_when : 何時做的
aname : 對哪個teacher name做動作

建立資料表 : 

CREATE TABLE teachers (
tid INT PRIMARY KEY,
tname VARCHAR(50) NOT NULL
);

CREATE TABLE audit_teacher (
aid BIGINT PRIMARY KEY AUTO_INCREMENT,
act_what VARCHAR(50),
act_who VARCHAR(50),
act_when DATETIME,
aname VARCHAR(50)
);

建立觸發器 :

以下列語法建立teachers_after_insert觸發器。

DELIMITER //
CREATE TRIGGER teachers_after_insert
AFTER INSERT
ON teachers
FOR EACH ROW
Insert into audit_teacher(act_what, act_who, act_when, aname) VALUES (concat('A  ', convert(NEW.tid,char)),  user(), now(), NEW.tname) //
DELIMITER ;

以下列語法建立teachers_after_update觸發器。

DELIMITER //
CREATE TRIGGER teachers_after_update
AFTER UPDATE
ON teachers
FOR EACH ROW
Insert into audit_teacher(act_what, act_who, act_when, aname) values (concat('U ',convert(OLD.tid,char)), USER(),NOW(), OLD.tname) //
DELIMITER ;

以下列語法建立teachers_after_delete觸發器。

DELIMITER //
CREATE TRIGGER teachers_after_delete
AFTER DELETE
ON teachers
FOR EACH ROW
Insert into audit_teacher(act_what, act_who, act_when, aname) values (concat('D ',convert(OLD.tid,char)), USER(),NOW(), OLD.tname);//
DELIMITER ;

然後操作資料,看看結果如何。

insert into teachers values (1, 'Tom Smith');
update teachers set tname='Tom Tom' where tid=1;
delete from teachers where tid=1;

Select * from audit_teacher; 來看看log的紀錄資料。

如下圖 : 



張貼留言

0 留言