數據庫編程技術10——觸發器及事務和鎖

一、觸發器

觸發器(trigger)是SQL server 提供給程序員和數據分析員來保證數據完整性的一種方法,它是與表 事件相關的特殊的存儲過程,它的執行不是由程序調用,也不是手工啓動,而是由事件來觸發,當對一個表 進行操作( insert,delete, update)時就會激活它執行。觸發器經常用於加強數據的完整性約束和業務 規則等。 觸發器可以從 DBA_TRIGGERS ,USER_TRIGGERS 數據字典中查到。 【觸發器和存儲過程的區別】 觸發器與存儲過程的區別是運行方式的不同,觸發器不能執行EXECUTE語句調用,而是在用戶執行 Transact-SQL語句時自動觸發執行而存儲過程需要用戶,應用程序或者觸發器來顯示地調用並執行。

【觸發器的優點】

a.觸發器是自動的。當對錶中的數據做了任何修改之後立即被激活。 b.觸發器可以通過數據庫中的相關表進行層疊修改。 c.觸發器可以強制限制。這些限制比用CHECK約束所定義的更復雜。與CHECK約束不同的是,觸發器可 以引用其他表中的列。

【觸發器的作用】

觸發器的主要作用就是其能夠實現由主鍵和外鍵所不能保證的複雜參照完整性和數據的一致性,它能夠 對數據庫中的相關表進行級聯修改,提高比CHECK約束更復雜的的數據完整性,並自定義錯誤消息。 觸發器的主要作用主要有以下接個方面: 強制數據庫間的引用完整性 級聯修改數據庫中所有相關的表,自動觸發其它與之相關的操作 跟蹤變化,撤銷或回滾違法操作,防止非法修改數據 返回自定義的錯誤消息,約束無法返回信息,而觸發器可以 觸發器可以調用更多的存儲過程

【觸發器的分類】

SqlServer包括三種常規類型的觸發器:DML觸發器、DDL觸發器和登錄觸發器。

1.DML(數據操作語言,Data Manipulation Language)觸發器

DML觸發器是一些附加在特定表或視圖上的操作代碼,當數據庫服務器中發生數據操作語言事件時執 行這些操作。

SqlServer中的DML觸發器有三種:
insert觸發器:向表中插入數據時被觸發;
delete觸發器:從表中刪除數據時被觸發;
update觸發器:修改表中數據時被觸發。

當遇到下列情形時,應考慮使用DML觸發器: 通過數據庫中的相關表實現級聯更改 防止惡意或者錯誤的insert、update和delete操作,並強制執行check約束定義的限制更爲複雜的其他 限制。 評估數據修改前後表的狀態,並根據該差異才去措施。

2.DDL(數據定義語言,Data Definition Language)觸發器

DDL觸發器是當服務器或者數據庫中發生數據定義語言(主要是以create,drop,alter開頭的語句)事件時 被激活使用,使用DDL觸發器可以防止對數據架構進行的某些更改或記錄數據中的更改或事件操作。

【觸發器的工作原理】

觸發器觸發時: 系統自動在內存中創建deleted表或inserted表;只讀,不允許修改,觸發器執行完成後,自動刪 除。
inserted表: 臨時保存了插入或更新後的記錄行; 可以從inserted表中檢查插入的數據是否滿足業務需求; 如果不滿足,則向用戶發送報告錯誤消息,並回滾插入操作。
deleted表: 臨時保存了刪除或更新前的記錄行; 可以從deleted表中檢查被刪除的數據是否滿足業務需求; 如果不滿足,則向用戶報告錯誤消息,並回滾插入操作。

【創建觸發器】

 CREATE TRIGGER trigger_name 
 ON table_name 
 [WITH ENCRYPTION]
  FOR | AFTER | INSTEAD OF [DELETE, INSERT, UPDATE] 
 AS
 GO 

T-SQL語句
– with encryption 表示加密觸發器定義的sql文本
– delete,insert,update指定觸發器的類型

二、事務和鎖

事務是SQL Server中單個的邏輯工作單元,該單元被作爲一個整體進行處理,事務保證連續多個操 作必須全部執行成功,否則必須立即恢復到任何操作執行前的狀態,即執行事務的結果是要麼全部將數據所 要執行的操作完成,要麼全部數據修改。
事務作爲單個邏輯工作單元執行的一系列操作,事務的處理必須滿足ACID原則。

BEGIN TRAN:設置起始點。
COMMIT TRAN:使事務成爲數據庫中永久的、不可逆轉的一部分。 ROLLBACK TRAN:本質上說想要忘記它曾經發生過。
SAVE TRAN:創建一個特定標記符,只允許部分回滾。

鎖是一種防止在某對象執行動作的一個進程與已在該對象上執行的其他進行相沖突的機制。也就是說, 如果有其他人在操作某個對象,那麼你舊不能在該對象上進行操作。你能否執行操作取決於其他用戶正在進 行的操作。

代碼示例

use testdbs
go

-- 一、操作之前準備的數據

CREATE TABLE student 
(
   stno char(6) NOT NULL PRIMARY KEY,
   stname char(8) NOT NULL,
   stsex char(2) NOT NULL,
   stbirthday date NOT NULL,
   speciality char(12) NULL,
   tc int NULL
)
GO

CREATE TABLE course
(
   cno char(3) NOT NULL PRIMARY KEY,
   cname char(16) NOT NULL,
   credit int NULL,
   tno char (6) NULL,
)
GO

CREATE TABLE score
(
   stno char (6) NOT NULL,
   cno char(3) NOT NULL,
   grade int NULL,
   PRIMARY KEY(stno,cno)
)
GO

CREATE TABLE teacher
(
   tno char (6) NOT NULL PRIMARY KEY,
   tname char(8) NOT NULL,
   tsex char (2) NOT NULL,
   tbirthday date NOT NULL,
   title char (12) NULL,
   school char (12) NULL
)
GO

-- 測試數據
INSERT INTO student values('121001','李賢友','男','1991-12-30','通信工程',72),
('121002','周映雪','女','1993-01-12','通信工程',89),
('121005','劉剛','男','1992-07-05','通信工程',59),
('122001','郭德強','男','1991-10-23','計算機',58),
('122002','謝萱','女','1992-09-11','計算機科',77),
('122004','孫婷','女','1992-02-24','計算機',63);

INSERT INTO student values('121015','晴天','男','2000-12-30','軟件工程',92),
('121022','MM','女','1983-01-12','數學',99),
('121065','大貓','男','1902-07-05','物理',86),
('122031','小強','男','1981-10-23','數學',78),
('122302','周芳','女','1982-09-11','物理',88),
('122804','王鑫','女','1983-02-24','軟件工程',79);
GO

INSERT INTO course values('102','數字電路',3,'102101'), ('203','數據庫系統',3,'204101'),
('205','微機原理',4,'204107'),('208','計算機網絡',4,NULL),('801','高等數學',4,'801102')
GO

INSERT INTO score values('121001','102',92),('121002','102',72),('121005','102',87),('122002','203',94),
('122004','203',81),('121001','205',91),('121002','205',65),('121005','205',85),('121001','801',94),
('121002','801',73),('121005','801',82),('122001','801',NULL),('122002','801',95),('122004','801',86);
GO

INSERT INTO teacher values('102101','劉林卓','男','1962-03-21','教授','通信學院'),
('102105','周學莉','女','1977-10-05','講師','通信學院'),
('204101','吳波','男','1978-04-26','教授','計算機學院'),
('204107','王冬琴','女','1968-11-18','副教授','計算機學院'),
('801102','李偉','男','1975-08-19','副教授','數學學院');
GO

-- 查詢數據
select *from student
select *from course
select *from score
select *from teacher

----------------------------------------------------------------------------------------------------
-- 二、使用T-SQL創建觸發器
select *from student
go

-- 1、在student表上創建觸發器trig_student,在student表中插入、刪除、修改數據它會自動顯示所有記錄
-- create trigger必須是批處理的第一條語句,此處go不能缺少
create trigger trig_student
	on student
after insert,delete,update
as
begin
	set nocount on	--在存儲過程中經常使用到的,阻止在結果集中顯示受T-SQL語句或則USP影響的行計數信息:set nocount on不返回計算,set nocount off返回計數
	select *from student
end
go

-- 測試數據
insert into student values('126088','小張','女','2003-8-8','文祕',78)
go

-- 2、在student表上創建insert觸發器,當向student插入數據時如果姓名發生重複時,則迴流到插入之前的操作
create trigger trig_studentinsert
	on student
	after insert
as
begin
	declare @nm char(8)
	select @nm=inserted.stname from inserted
	if exists(select stname from student where stname=@nm)	
		begin 
		print '對不起,數據中存儲此姓名,請不要重複插入.'
		rollback transaction
		end
end

-- 測試數據
select *from student

INSERT INTO student(stno,stname,stsex,stbirthday) values('121888','青龍','男','2018-12-30')
go
-- 3、創建觸發器防止用戶修改學分

create trigger trig_updatestudent
	on student
after update
as
if UPDATE(tc)
	begin 
		print '數據表中學分非常重要,保密級別高,不允許用戶修改學分.'
		rollback transaction  -- 迴流之前的操作
	end
go

-- 測試數據
select *from student

update student 
set tc=50
where stno=121001
go

-- 4、在student表中,防止用戶刪除'通信工程'專業的學生記錄信息

create trigger trig_deletestudent
	on student
after delete
as
	if exists(select *from deleted where speciality='計算機')
		begin
		print '禁止刪除《計算機》專業的學生所有記錄.'
		rollback transaction
		end
go
-- 測試數據
select *from student

delete student
where speciality='計算機科'

-- 5、在course表上創建一個insetead of觸發器,當用戶向此表插入數據時顯示course表中的記錄
select *from course
go

create trigger trig_istd
	on course
instead of insert
as 
	select *from course
go

-- 測試數據
select *from course
go
insert into course(cno,cname) values('206','數據結構')
go


----------------------------------------------------------------------------------------------------
-- 二、創建和使用DDL觸發器
-- 1、創建一個解發器,防止用戶對數據庫中任何一個表進行修改或刪除 
create trigger trig_db
	on database
after drop_table,alter_table
as
	begin
	print '不能修改數據表的結構'
	rollback transaction	-- 回滾之前操作
	end
go

-- 測試數據
select *from student
go

alter table student add class int
go

-- 刪除DML觸發器
-- drop trigger 觸發器的名稱

-- 刪除DDL觸發器
-- drop trigger 觸發器的名稱 on database


----------------------------------------------------------------------------------------------------
-- 三、事務處理
use bank
go

-- 查詢數據
select *from tb_bank
go

-- 插入兩條數據
insert into tb_bank values('1111222233334444678','曹操',100000)
insert into tb_bank values('1111222233334444123','劉備',50000)

-- 查詢數據
select *from tb_bank
go

-- 事務銀行轉賬操作
-- 查詢數據
select *from tb_bank
go

begin tran tran_bankmoney	--開始事務
declare @tran_error int;
set @tran_error=0
	begin try
		update tb_bank set accountbalance=accountbalance-150000 where accountname='曹操'
		set @tran_error=@tran_error+@@ERROR
		-- 測試出錯代碼,查詢曹操的錢減少15000,劉備的錢是否增加15000
		-- set @tran_error=1
		update tb_bank set accountbalance=accountbalance+150000 where accountname='劉備'
		set @tran_error=@tran_error+@@ERROR
	end try
begin catch
	print '出現異常,錯誤編號:'+convert(varchar,error_number()) +',錯誤消息:'+error_message()
	set @tran_error=@tran_error+1
end catch

if (@tran_error>0)
	begin
		rollback tran
		print '轉賬失敗,取消本次交易!'
	end
else
	begin
		commit tran
		print '轉賬成功,提交數據庫!'
	end
go

-- 測試數據
select *from tb_bank
go

-- 鎖
-- 鎖定是SQL Server用來同步多個用戶同時對同一個數據塊的訪問的一種機制,用於控制多個用戶的併發操作,
-- 以防止用戶讀取到由其他用戶更改的數據或者多個用戶同時修改同一數據。從而確保事務的完整性和數據庫的一致性。

-- 鎖模式:共享鎖(S鎖)、更新鎖(U鎖)、排他鎖、意向鎖、架構鎖、大容量更新(BU)鎖、鍵範圍鎖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章