-- 作者:彭建軍
-- 日期:2005-06-27
-- 頁面:Page3
-- 概要:觸發器的創建、修改、刪除以及調用
-- 說明:以下示例均在[查詢分析器]下進行,可配合圖形界面進行測試
--================================================--
/*
建立虛擬測試環境,包含:表[捲菸庫存表],表[捲菸銷售表]。
請大家注意跟蹤這兩個表的數據,體會觸發器到底執行了什麼業務邏輯,對數據有什麼影響。
爲了能更清晰的表述觸發器的作用,表結構存在數據冗餘,且不符合第三範式,這裏特此說明。
*/
USE Master
GO
IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = 'U' AND NAME = '捲菸庫存表')
DROP TABLE 捲菸庫存表
GO
IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = 'U' AND NAME = '捲菸銷售表')
DROP TABLE 捲菸銷售表
GO
--業務規則:銷售金額 = 銷售數量 * 銷售單價 業務規則。
CREATE TABLE 捲菸銷售表
(
捲菸品牌 VARCHAR(40) PRIMARY KEY NOT NULL,
購貨商 VARCHAR(40) NULL,
銷售數量 INT NULL,
銷售單價 MONEY NULL,
銷售金額 MONEY NULL
)
GO
--業務規則:庫存金額 = 庫存數量 * 庫存單價 業務規則。
CREATE TABLE 捲菸庫存表
(
捲菸品牌 VARCHAR(40) PRIMARY KEY NOT NULL,
庫存數量 INT NULL,
庫存單價 MONEY NULL,
庫存金額 MONEY NULL
)
GO
--創建觸發器,示例1
/*
創建觸發器[T_INSERT_捲菸庫存表],這個觸發器較簡單。
說明: 每當[捲菸庫存表]發生 INSERT 動作,則引發該觸發器。
觸發器功能: 強制執行業務規則,保證插入的數據中,庫存金額 = 庫存數量 * 庫存單價。
注意: [INSERTED]、[DELETED]爲系統表,不可創建、修改、刪除,但可以調用。
重要: 這兩個系統表的結構同插入數據的表的結構。
*/
IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = 'TR' AND NAME = 'T_INSERT_捲菸庫存表')
DROP TRIGGER T_INSERT_捲菸庫存表
GO
CREATE TRIGGER T_INSERT_捲菸庫存表
ON 捲菸庫存表
FOR INSERT
AS
--提交事務處理
BEGIN TRANSACTION
--強制執行下列語句,保證業務規則
UPDATE 捲菸庫存表
SET 庫存金額 = 庫存數量 * 庫存單價
WHERE 捲菸品牌 IN (SELECT 捲菸品牌 from INSERTED)
COMMIT TRANSACTION
GO
/*
針對[捲菸庫存表],插入測試數據:
注意,第一條數據(紅塔山新勢力)中的數據符合業務規則,
第二條數據(紅塔山人爲峯)中,[庫存金額]空,不符合業務規則,
第三條數據(雲南映像)中,[庫存金額]不等於[庫存數量]乘以[庫存單價],不符合業務規則。
第四條數據庫存數量爲0。
請注意在插入數據後,檢查[捲菸庫存表]中的數據是否 庫存金額 = 庫存數量 * 庫存單價。
*/
INSERT INTO 捲菸庫存表(捲菸品牌,庫存數量,庫存單價,庫存金額)
SELECT '紅塔山新勢力',100,12,1200 UNION ALL
SELECT '紅塔山人爲峯',100,22,NULL UNION ALL
SELECT '雲南映像',100,60,500 UNION ALL
SELECT '玉溪',0,30,0
GO
--查詢數據
SELECT * FROM 捲菸庫存表
GO
/*
結果集
RecordId 捲菸品牌 庫存數量 庫存單價 庫存金額
-------- ------------ -------- ------- ---------
1 紅塔山新勢力 100 12.0000 1200.0000
2 紅塔山人爲峯 100 22.0000 2200.0000
3 雲南映像 100 60.0000 6000.0000
4 玉溪 0 30.0000 .0000
(所影響的行數爲 4 行)
*/
--觸發器示例2
/*
創建觸發器[T_INSERT_捲菸銷售表],該觸發器較複雜。
說明: 每當[捲菸庫存表]發生 INSERT 動作,則引發該觸發器。
觸發器功能: 實現業務規則。
業務規則: 如果銷售的捲菸品牌不存在庫存或者庫存爲零,則返回錯誤。
否則則自動減少[捲菸庫存表]中對應品牌捲菸的庫存數量和庫存金額。
*/
IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = 'TR' AND NAME = 'T_INSERT_捲菸銷售表')
DROP TRIGGER T_INSERT_捲菸銷售表
GO
CREATE TRIGGER T_INSERT_捲菸銷售表
ON 捲菸銷售表
FOR INSERT
AS
BEGIN TRANSACTION
--檢查數據的合法性:銷售的捲菸是否有庫存,或者庫存是否大於零
IF NOT EXISTS (
SELECT 庫存數量
FROM 捲菸庫存表
WHERE 捲菸品牌 IN (SELECT 捲菸品牌 FROM INSERTED)
)
BEGIN
--返回錯誤提示
RAISERROR('錯誤!該捲菸不存在庫存,不能銷售。',16,1)
--回滾事務
ROLLBACK
RETURN
END
IF EXISTS (
SELECT 庫存數量
FROM 捲菸庫存表
WHERE 捲菸品牌 IN (SELECT 捲菸品牌 FROM INSERTED) AND
庫存數量 <= 0
)
BEGIN
--返回錯誤提示
RAISERROR('錯誤!該捲菸庫存小於等於0,不能銷售。',16,1)
--回滾事務
ROLLBACK
RETURN
END
--對合法的數據進行處理
--強制執行下列語句,保證業務規則
UPDATE 捲菸銷售表
SET 銷售金額 = 銷售數量 * 銷售單價
WHERE 捲菸品牌 IN (SELECT 捲菸品牌 FROM INSERTED)
DECLARE @捲菸品牌 VARCHAR(40)
SET @捲菸品牌 = (SELECT 捲菸品牌 FROM INSERTED)
DECLARE @銷售數量 MONEY
SET @銷售數量 = (SELECT 銷售數量 FROM INSERTED)
UPDATE 捲菸庫存表
SET 庫存數量 = 庫存數量 - @銷售數量,
庫存金額 = (庫存數量 - @銷售數量)*庫存單價
WHERE 捲菸品牌 = @捲菸品牌
COMMIT TRANSACTION
GO
--請大家自行跟蹤[捲菸庫存表]和[捲菸銷售表]的數據變化。
--針對[捲菸銷售表],插入第一條測試數據,該數據是正常的。
INSERT INTO 捲菸銷售表(捲菸品牌,購貨商,銷售數量,銷售單價,銷售金額)
SELECT '紅塔山新勢力','某購貨商',10,12,1200
GO
--針對[捲菸銷售表],插入第二條測試數據,該數據 銷售金額 不等於 銷售單價 * 銷售數量。
--觸發器將自動更正數據,使 銷售金額 等於 銷售單價 * 銷售數量。
INSERT INTO 捲菸銷售表(捲菸品牌,購貨商,銷售數量,銷售單價,銷售金額)
SELECT '紅塔山人爲峯','某購貨商',10,22,2000
GO
--針對[捲菸銷售表],插入第三條測試數據,該數據中的捲菸品牌在 捲菸庫存表 中找不到對應。
--觸發器將報錯。
INSERT INTO 捲菸銷售表(捲菸品牌,購貨商,銷售數量,銷售單價,銷售金額)
SELECT '紅河V8','某購貨商',10,60,600
GO
/*
結果集
服務器: 消息 50000,級別 16,狀態 1,過程 T_INSERT_捲菸銷售表,行 15
錯誤!該捲菸不存在庫存,不能銷售。
*/
--針對[捲菸銷售表],插入第三條測試數據,該數據中的捲菸品牌在 捲菸庫存表 中庫存爲0。
--觸發器將報錯。
INSERT INTO 捲菸銷售表(捲菸品牌,購貨商,銷售數量,銷售單價,銷售金額)
SELECT '玉溪','某購貨商',10,30,300
GO
/*
結果集
服務器: 消息 50000,級別 16,狀態 1,過程 T_INSERT_捲菸銷售表,行 29
錯誤!該捲菸庫存小於等於0,不能銷售。
*/
--查詢數據
SELECT * FROM 捲菸庫存表
SELECT * FROM 捲菸銷售表
GO
/*
補充:
1、本示例主要通過一個簡單的業務規則實現來進行觸發器使用的說明,具體的要根據需要靈活處理;
2、關於觸發器要理解並運用好 INSERTED ,DELETED 兩個系統表;
3、本示例創建的觸發器都是 FOR INSERT ,具體的語法可參考:
Trigger語法
CREATE TRIGGER trigger_name
ON { table | view }
[ WITH ENCRYPTION ] --用於加密觸發器
{
{ { FOR | AFTER | INSTEAD OF } { [ INSERT ] [ , ] [ UPDATE ] }
[ WITH APPEND ]
[ NOT FOR REPLICATION ]
AS
[ { IF UPDATE ( column )
[ { AND | OR } UPDATE ( column ) ]
[ ...n ]
| IF ( COLUMNS_UPDATED ( ) { bitwise_operator } updated_bitmask )
{ comparison_operator } column_bitmask [ ...n ]
} ]
sql_statement [ ...n ]
}
}
4、關於觸發器,還應該注意
(1)、DELETE 觸發器不能捕獲 TRUNCATE TABLE 語句。
(2)、觸發器中不允許以下 Transact-SQL 語句:
ALTER DATABASE CREATE DATABASE DISK INIT
DISK RESIZE DROP DATABASE LOAD DATABASE
LOAD LOG RECONFIGURE RESTORE DATABASE
RESTORE LOG
(3)、觸發器最多可以嵌套 32 層。
*/
--修改觸發器
--實質上,是將 CREATE TRIGGER ... 修改爲 ALTER TRIGGER ...即可。
--刪除觸發器
DROP TRIGGER xxx
GO
--刪除測試環境
DROP TABLE 捲菸庫存表
GO
DROP TABLE 捲菸銷售表
GO
DROP TRIGGER T_INSERT_捲菸庫存表
GO
DROP TRIGGER T_INSERT_捲菸銷售表
GO
/*
觸發器:出入庫操作(單據寫入)時自動更新物料的當前庫存數字
作者:[email protected]
日期:2005年12月4日 18:26
*/
CREATE TRIGGER [TRI_CRK2KC_INSERT] ON [dbo].[CK]
AFTER INSERT
AS
BEGIN
DECLARE @CRK int,@PJBH varchar(20),@SL int --定義變量:出入庫標誌、單據編號、出入庫標數量
SELECT @CRK=[CRK],@PJBH=[PJBH],@SL=[SL] FROM [INSERTED] --賦值:出入庫單據寫入時的入庫標誌、單據編號、出入庫標數量
IF @CRK=1 --如果爲入庫,當前庫存增入庫數
UPDATE [WLB] SET [KC]=[KC]+@SL WHERE [BM]=@PJBH
ELSE --如果爲出庫,當前庫存減出庫數
UPDATE [WLB] SET [KC]=[KC]-@SL WHERE [BM]=@PJBH
END
---------------------------------------------------
/*
觸發器:出入庫操作(單據刪除)時自動更新物料的當前庫存數字
作者:[email protected]
日期:2005年12月4日 18:26
*/
CREATE TRIGGER [TRI_CRK2KC_DELETE] ON [dbo].[CK]
FOR DELETE
AS
BEGIN
DECLARE @CRK int,@PJBH varchar(20),@SL int --定義變量:出入庫標誌、單據編號、出入庫標數量
SELECT @CRK=[CRK],@PJBH=[PJBH],@SL=[SL] FROM [DELETED] --賦值:出入庫單據刪除時的入庫標誌、單據編號、出入庫標數量
IF @CRK=1 --如果爲入庫單據刪除,當前庫存減原入庫數
UPDATE [WLB] SET [KC]=[KC]-@SL WHERE [BM]=@PJBH
ELSE --如果爲出庫單據刪除,當前庫存增原出庫數
UPDATE [WLB] SET [KC]=[KC]+@SL WHERE [BM]=@PJBH
END
---------------------------------------------------
/*
觸發器:出入庫操作(單據修改)時自動更新物料的當前庫存數字
作者:[email protected]
日期:2005年12月4日 18:26
*/
CREATE TRIGGER [TRI_CRK2KC_UPDATE] ON [dbo].[CK]
FOR UPDATE
AS
BEGIN
DECLARE @CRK int,@PJBH varchar(20),@SL int --定義變量:出入庫標誌、單據編號、出入庫標數量
SELECT @CRK=[CRK],@PJBH=[PJBH],@SL=[SL] FROM [DELETED] --賦值:出入庫單據修改前的入庫標誌、單據編號、出入庫標數量
IF @CRK=1 --如果爲入庫單據修改,先讓當前庫存減原入庫數
UPDATE [WLB] SET [KC]=[KC]-@SL WHERE [BM]=@PJBH
ELSE --如果爲出庫單據修改,先讓當前庫存增原出庫數
UPDATE [WLB] SET [KC]=[KC]+@SL WHERE [BM]=@PJBH
SELECT @CRK=[CRK],@PJBH=[PJBH],@SL=[SL] FROM [INSERTED] --賦值:出入庫單據修改後的入庫標誌、單據編號、出入庫標數量
IF @CRK=1 --如果爲入庫單據修改,然後讓當前庫存增入庫數
UPDATE [WLB] SET [KC]=[KC]+@SL WHERE [BM]=@PJBH
ELSE --如果爲出庫單據修改,然後讓當前庫存減出庫數
UPDATE [WLB] SET [KC]=[KC]-@SL WHERE [BM]=@PJBH
END
---------------------------------------------------
/*
觸發器:修改WLB的期初庫存時自動更新物料的當前庫存數字
作者:[email protected]
日期:2005年12月4日 18:59
*/
CREATE TRIGGER [TRI_QC2KC_UPDATE] ON [dbo].[WLB]
FOR UPDATE
AS
IF UPDATE([QC]) --僅當WLB的期初庫存被修改時才觸發,其他字段修改時不觸發
BEGIN
DECLARE @BM varchar(20),@QC_1 int,@QC_2 int --定義變量:物料編號(在WLB表中應唯一)、期初庫存修改前值、期初庫存修改後值
SELECT @QC_1=[QC] FROM [DELETED] --賦值:期初庫存修改前的物料編號、期初庫存
SELECT @BM=[BM],@QC_2=[QC] FROM [INSERTED] --賦值:期初庫存修改後的物料編號、期初庫存
IF @QC_1<>@QC_2 --期初庫存修改前、後值不等時才修改當前庫存數字;否則無動作
UPDATE [WLB] SET [KC]=[KC]+(@QC_2-@QC_1) WHERE [BM]=@BM
END
---------------------------------------------------