2010-12-02 107 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

相关问题