2010-12-02 81 views
3

計劃:使用INSTEAD OF INSERT觸發器將失敗的插入重定向到「掛起」表。這些行保留在「待處理」表中,直到某些附加信息被插入另一個表中;當這個新的信息可用時,掛起的行被移動到它們的原始目的地。INSTEAD OF UPDATE的INSERTED表爲什麼爲空?

背景:交易記錄與持倉相關。更新交易的服務可以具有尚未存在於數據庫中的信息,例如尚未被插入的持有人的交易(請不要關注該系統的那部分「爲什麼」,我可以'改變這一點)。

問題:INSTEAD OF INSERT觸發器工作,但我與INSTEAD OF UPDATE觸發器有問題。當應用UPDATE,但要更新的行位於「待處理」表中時,觸發器中的INSERTED表爲空,因此我無法更新「待處理」表。這裏是(簡化)DDL:

CREATE TABLE [Holding] (
    [HoldingID] INTEGER NOT NULL, 
    [InstrumentID] INTEGER, 
    CONSTRAINT [PK_Holding] PRIMARY KEY ([HoldingID]) 
) 
GO 
CREATE TABLE [Trade] (
    [TradeID] INTEGER IDENTITY(1,1) NOT NULL, 
    [HoldingID] INTEGER NOT NULL, 
    [BuySell] CHAR(1) NOT NULL, 
    CONSTRAINT [PK_TradeSummary] PRIMARY KEY ([TradeID]) 
) 
GO 
ALTER TABLE [Trade] ADD CONSTRAINT [CC_Trade_BuySell] 
    CHECK (BuySell = 'B' or BuySell = 'S') 
GO 
ALTER TABLE [Trade] ADD CONSTRAINT [Holding_Trade] 
    FOREIGN KEY ([HoldingID]) REFERENCES [Holding] ([HoldingID]) 
GO 
CREATE TABLE [TradePending] (
    [TradeID] INTEGER IDENTITY(1,1) NOT NULL, 
    [HoldingID] INTEGER NOT NULL, 
    [BuySell] CHAR(1) NOT NULL, 
    CONSTRAINT [PK_TradePending] PRIMARY KEY ([TradeID]) 
) 
GO 
ALTER TABLE [TradePending] ADD CONSTRAINT [CC_TradePending_BuySell] 
    CHECK (BuySell = 'B' or BuySell = 'S') 
GO 
-- The INSERT trigger works, when the referenced holding does not exist the row is redirected to the TradePending table. 
CREATE TRIGGER [Trg_Trade_Insert] 
ON [Trade] 
INSTEAD OF INSERT 
AS 
IF NOT EXISTS (SELECT 1 
    FROM inserted i INNER JOIN Holding h 
    ON i.HoldingID = h.HoldingID) 
BEGIN 
    INSERT TradePending(HoldingID, BuySell) SELECT HoldingID, BuySell FROM inserted 
END 
ELSE 
BEGIN 
    INSERT Trade(HoldingID, BuySell) SELECT HoldingID, BuySell FROM inserted 
END 
GO 

扳機,做UPDATE作品當Trade表中存在該行,但不是在該行不存在,INSERTED虛表是空的。我在觸發器中添加了一些PRINT語句,以查看發生了什麼。

CREATE TRIGGER [dbo].[Trg_Trade_Update] 
ON [dbo].[Trade] 
INSTEAD OF UPDATE 
AS 

DECLARE @s char(1) 
DECLARE @h int 

IF NOT EXISTS (SELECT 1 
    FROM inserted i INNER JOIN Trade t 
    ON i.HoldingID = t.HoldingID) 
BEGIN 
    PRINT 'Update TradePending' 

SET @h = COALESCE((SELECT i.HoldingID 
    FROM TradeSummaryPending t INNER JOIN inserted i 
     ON t.HoldingID = i.HoldingID), 0) 
SET @a = COALESCE((SELECT i.BuySell 
    FROM TradeSummaryPending t INNER JOIN inserted i 
     ON t.HoldingID = i.HoldingID), 'N') 
    PRINT 'h=' + CAST(@h AS varchar(1)) + ' s=' + @s 

    UPDATE TradePending 
    SET BuySell = i.BuySell 
    FROM Trade t INNER JOIN inserted i 
     ON t.HoldingID = i.HoldingID 
END 
ELSE 
BEGIN 
    PRINT 'Update Trade'  
    SET @h = (SELECT i.HoldingID 
     FROM Trade t INNER JOIN inserted i 
      ON t.HoldingID = i.HoldingID) 
    SET @s = (SELECT i.BuySell 
     FROM Trade t INNER JOIN inserted i 
      ON t.HoldingID = i.HoldingID) 
    PRINT 'h=' + CAST(@h AS varchar(1)) + ' s=' + @s 

    UPDATE Trade  
    SET BuySell = i.BuySell 
    FROM Trade t INNER JOIN inserted i 
     ON t.HoldingID = i.HoldingID 
END 

下面是一些樣本數據來進行測試:

-- Create a Holding and a Trade, this will be inserted as normal. 
INSERT Holding VALUES(1,100) 
INSERT TradeSummary VALUES(1,'B') 

-- Create a Trade where the Holding does not exists, 
-- row redirected to TradePending table. 
INSERT TradeSummary values(2,'S') 

-- Update the first trade to be a Buy, updates the `Trade` table 
UPDATE Trade SET BuySell = 'S' WHERE HoldingID = 1 

從執行更新的輸出:

Update Trade 
h=1 s=S 

(1 row(s) affected)  
(1 row(s) affected) 

現在更新只在TradePending表中存在的行:

UPDATE Trade SET BuySell = 'B' WHERE HoldingID = 2 

這會導致以下的輸出:

Update TradePending 
h=0 s=N 

(0 row(s) affected) 
(0 row(s) affected) 

INSERTED表出現現在包含行,即使這是一個INSTEAD OF觸發器和SQL應用到表之前應執行。

任何人都可以解釋爲什麼INSERTED表是空的?我敢肯定,解決方案將是一件小事,但我似乎無法得到它的工作。

回答

3

當然的行不存在於插入的假表,當你更新行時,做表不存在下手:你Trade發出UPDATE語句對於那些在TradePending行!

此外,您的INSTEAD OF INSERT觸發器已損壞。它只適用於單行插入,即使對於那些在併發下也會失敗的行。使用基於集合的MERGE。

最終,您正在設計一個與應用程序無關的數據模型的黑客攻擊。創建INSTEAD OF觸發器以徹底改變遺留代碼使用的表格的形狀只能工作到目前爲止,您遇到的這個問題僅僅是下一個問題中的一個。最終,您的客戶端代碼必須插入/更新/刪除正確的表格。

作爲一種變通方法,你可以嘗試所有的數據移動到保存貿易和TradePending和使用狀態欄來區分這兩個表,揭露了舊貿易和TradePending表作爲意見和使用觸發器來捕捉視圖上的DML將它們重定向到適當的表格。不知道是否會工作,但我現在無法測試它。

更新:

下面是一個例子如何做到這一點與更新視圖的工作:

CREATE TABLE [Holding] (
    [HoldingID] INTEGER NOT NULL, 
    [InstrumentID] INTEGER, 
    CONSTRAINT [PK_Holding] PRIMARY KEY ([HoldingID]) 
) 
GO 

CREATE TABLE [TradeStorage] (
    [TradeID] INTEGER IDENTITY(1,1) NOT NULL, 
    [HoldingID] INTEGER NOT NULL, 
    [BuySell] CHAR(1) NOT NULL, 
    CONSTRAINT [PK_TradeSummary] PRIMARY KEY ([TradeID]) 
    , CONSTRAINT [CC_Trade_BuySell] CHECK (BuySell IN ('B','S')) 
    ) 
GO 

create view Trade 
with schemabinding 
as 
select TradeID, HoldingID, BuySell 
from dbo.TradeStorage 
where exists (
    select HoldingID from dbo.Holding 
    where Holding.HoldingID = TradeStorage.HoldingID); 
go 

create view TradePending 
with schemabinding 
as 
select TradeID, HoldingID, BuySell 
from dbo.TradeStorage 
where not exists (
    select HoldingID from dbo.Holding 
    where HoldingID = TradeStorage.HoldingID); 
go 

-- Create a Holding and a Trade, this will be inserted as normal. 
INSERT Holding VALUES(1,100) 
INSERT Trade VALUES(1,'B') 

-- Create a Trade where the Holding does not exists, 
-- row redirected to TradePending table. 
INSERT Trade values(2,'B') 
go 

select * from Trade; 
select * from TradePending; 
go 

-- Update the first trade to be a Buy, updates the `Trade` table 
UPDATE Trade SET BuySell = 'S' WHERE HoldingID = 1 
go 

-- Insert a holding with ID 2, 
-- this will automatically move the pending trade to Trade 
INSERT Holding VALUES(2,100) 

select * from Trade; 
select * from TradePending; 
go 

UPDATE Trade SET BuySell = 'S' WHERE HoldingID = 2 
go 

select * from Trade; 
select * from TradePending; 
go 

需要注意的是目前還沒有可以更新貿易爲處於TradePending記錄。沒有觸發器,視圖或類似機制可以這樣做。

0

我還沒有時間來運行這個,但你確定插入的表是空的嗎? (您總是加入到其他表中,因此這些表中缺少記錄可能會導致結果集中的行被壓縮。)刪除的內容如何?對於更新,您應該有一個插入和刪除的設置。

+0

我加入到貿易或TradePending表取決於IF EXISTS檢查的結果,因爲我的理解是你必須加入到INSERTED表才能訪問它(或者我錯誤地認爲?)要測試你的理論我把「選擇計數(*)從插入」,計數= 0.打印同樣從刪除,計數= 0. – Tony 2010-12-02 14:29:31

+0

好吧,我錯了需要加入到INSERTED訪問它。我改變了更新到TradePending表是:「UPDATE TradePending \t SET BuySell =(SELECT BuySell FROM插入 \t \t WHERE inserted.HoldingID = TradePending.HoldingID) 和解析器很樂意這麼做,但它運行的時候,我得到錯誤:「無法將NULL值插入'BuySell'列' – Tony 2010-12-02 14:36:04

相關問題