觸發器,出入庫操作(單據寫入)時自動更新物料的當前庫存數字

--  作者:彭建軍
--  日期: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

---------------------------------------------------

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