首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server 多表視圖更新觸發器實戰:INSTEAD OF 觸發器實現跨表寫入同步

     編輯:關於SqlServer

核心原理

視圖本身不存儲數據。INSTEAD OF 觸發器 攔截對視圖的 DML 操作,透過 inserted/deleted 虛擬表獲取變更數據,手動分發更新至底層多張基礎表。

測試環境建立

1. 三張基礎表

-- 學生基本信息表
CREATE TABLE XINXIN_TAB (
    姓名 NVARCHAR(10) NOT NULL,
    性別 NVARCHAR(1) NOT NULL,
    學號 INT NOT NULL PRIMARY KEY CLUSTERED,
    班級 NVARCHAR(20) NOT NULL,
    出生日期 DATE NOT NULL
)

-- 明細分數表
CREATE TABLE FENSHU_TAB (
    學號 INT NOT NULL,
    語文 DECIMAL(3,1) NOT NULL,
    數學 DECIMAL(3,1) NOT NULL,
    英語 DECIMAL(3,1) NOT NULL
)

-- 綜合分數表
CREATE TABLE ZHONGHE_TAB (
    姓名 NVARCHAR(10) NOT NULL,
    學號 INT NOT NULL,
    總分 DECIMAL(4,1) NOT NULL,
    平均分 DECIMAL(3,1) NOT NULL
)

2. 測試數據

INSERT INTO XINXIN_TAB VALUES
('李曉峰', '男', 6080, '計算機', '2013-05-03'),
('李曉峰1', '男', 6081, '計算機1', '2013-05-04'),
('李曉峰2', '男', 6082, '計算機2', '2013-05-05'),
('李曉峰3', '男', 6083, '計算機3', '2013-05-06'),
('張曉', '女', 6084, '美術', '2013-05-07')

INSERT INTO FENSHU_TAB VALUES
(6080, 99.5, 98.6, 59.2),
(6081, 93.5, 94.3, 55.8),
(6082, 96.5, 78.6, 58.5),
(6083, 99.5, 68.4, 89.2),
(6084, 99.7, 98.7, 59.4)

-- 綜合表由程式計算填充
DECLARE @i INT = 6080
WHILE @i <= 6084
BEGIN
    DECLARE @zf DECIMAL(4,1), @pf DECIMAL(3,1), @xm NVARCHAR(10)
    SELECT @zf = 語文+數學+英語, @pf = (語文+數學+英語)/3.0, @xm = 姓名
    FROM XINXIN_TAB x JOIN FENSHU_TAB f ON x.學號=f.學號 WHERE x.學號=@i
    INSERT INTO ZHONGHE_TAB VALUES (@xm, @i, @pf, @zf)
    SET @i += 1
END

3. 多表關聯視圖

CREATE VIEW SHITU_FFENSHU_XINXI (姓名, 學號, 平均分, 總分, 班級, 出生日期)
AS
SELECT TOP 800 x.姓名, f.學號, z.平均分, z.總分, x.班級, x.出生日期
FROM XINXIN_TAB x
JOIN FENSHU_TAB f ON x.學號 = f.學號
JOIN ZHONGHE_TAB z ON f.學號 = z.學號
ORDER BY f.學號

直接更新視圖失敗演示

UPDATE SHITU_FFENSHU_XINXI SET 姓名='aaaaa', 平均分=111 WHERE 學號=6080

❌ 報錯:視圖 'SHITU_FFENSHU_XINXI' 不可更新,因為它修改了多個基礎表。

INSTEAD OF UPDATE 觸發器實現

CREATE TRIGGER TRIGG_UPDATE ON dbo.SHITU_FFENSHU_XINXI
INSTEAD OF UPDATE
AS
BEGIN
    DECLARE @姓名 NVARCHAR(10), @學號 INT, @平均分 DECIMAL(3,1), @總分 DECIMAL(4,1),
            @班級 NVARCHAR(20), @出生日期 DATE

    -- 遍歷 inserted 虛擬表(支援批次更新)
    DECLARE cur CURSOR LOCAL FAST_FORWARD FOR
        SELECT 姓名, 學號, 平均分, 總分, 班級, 出生日期 FROM inserted
    OPEN cur
    FETCH NEXT FROM cur INTO @姓名, @學號, @平均分, @總分, @班級, @出生日期
    WHILE @@FETCH_STATUS = 0
    BEGIN
        -- 更新基本信息表
        UPDATE XINXIN_TAB SET 姓名=@姓名, 班級=@班級, 出生日期=@出生日期 WHERE 學號=@學號
        -- 更新明細分數表(學號通常不變,示例保留同步邏輯)
        UPDATE FENSHU_TAB SET 學號=@學號 WHERE 學號=@學號
        -- 更新綜合分數表
        UPDATE ZHONGHE_TAB SET 平均分=@平均分, 總分=@總分 WHERE 學號=@學號

        FETCH NEXT FROM cur INTO @姓名, @學號, @平均分, @總分, @班級, @出生日期
    END
    CLOSE cur; DEALLOCATE cur
END

驗證測試

-- 更新前
SELECT * FROM SHITU_FFENSHU_XINXI WHERE 學號=6080

-- 執行更新
UPDATE dbo.SHITU_FFENSHU_XINXI
SET 姓名='liyifeng', 平均分=66.6, 總分=88.8, 班級='計算機SQLServer', 出生日期='2013-05-05'
WHERE 學號=6080

-- 更新後視圖
SELECT * FROM SHITU_FFENSHU_XINXI WHERE 學號=6080

-- 驗證三張基礎表
SELECT * FROM XINXIN_TAB WHERE 學號=6080
SELECT * FROM FENSHU_TAB WHERE 學號=6080
SELECT * FROM ZHONGHE_TAB WHERE 學號=6080

觸發器管理

-- 禁用
DISABLE TRIGGER dbo.TRIGG_UPDATE ON SHITU_FFENSHU_XINXI
-- 啟用
ENABLE TRIGGER dbo.TRIGG_UPDATE ON SHITU_FFENSHU_XINXI

關鍵設計要點

  • INSTEAD OF 而非 AFTER: 視圖不可直接更新,必須用 INSTEAD OF 完全接管寫入邏輯。
  • 游標遍歷 inserted: 支援批次更新(多行 WHERE 條件),單行變數賦值僅支援單行更新。
  • 欄位映射清晰: 視圖欄位 → 對應基礎表欄位 → 分別 UPDATE。
  • 事務一致性: 觸發器隱式在更新事務中,任一 UPDATE 失敗全盤回滾。
  • 謹慎使用: 觸發器隱蔽邏輯,除非必要(如舊系統視圖兼容),優先考慮預存程式/應用層分發更新。

總結

INSTEAD OF 觸發器讓「不可更新視圖」變為可更新,實現了單一視圖介面、多表寫入同步。核心模式:攔截 → 遍歷 inserted → 分發 UPDATE → 隱式事務保證一致性。生產環境建議配合 TRY...CATCH 記錄錯誤日誌,並嚴格控制權限。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved