2010-12-02 12 views
3

プラン:INSTEAD OF INSERTトリガーを使用して、失敗した挿入を「保留」テーブルにリダイレクトします。これらの行は、いくつかの追加情報が別のテーブルに挿入されるまで「保留中」テーブルに残ります。この新しい情報が利用可能になると、保留中の行は元の宛先に移動されます。INSTEAD OF UPDATEトリガのINSERTEDテーブルが空であるのはなぜですか?

背景:ホールディングに関連する取引が記録されます。トレードを更新するサービスは、まだ挿入されていないホールディングの取引(システムのその部分の「なぜ」に焦点を当ててはいけない、それを変更する)。

問題:トリガーは動作しますが、私はINSTEAD OF UPDATEトリガーで問題があります。 UPDATEが適用されているが、更新される行が '保留中'のテーブルにある場合、INSERTEDテーブルがトリガー内に空であるため、 '保留中'テーブルを更新できません。

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 

トリガ行は行が存在しない場合、INSERTED仮想テーブルが空であるTradeテーブル内に存在するがないUPDATE作品を行うには:ここでは(簡体字)DDLです。トリガーに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がテーブルに適用される前に実行する必要があるにもかかわらず、今の行を含むように表示されます。次の出力になり3210。

INSERTEDテーブルが空である理由を説明できる人はいますか?私は解決策が何か自明なものになると確信していますが、私はそれを働かせるようには見えません。

答えて

3

あなたはで始まるし、テーブルに存在しない行の更新時に行が挿入擬似テーブルに存在しません:あなたはTradePendingである行に対してTradeにUPDATE文を発行します!

さらに、INSTEAD OF INSERTトリガーが壊れています。これは単一行の挿入に対してのみ機能し、それらの場合でも並行処理では失敗します。集合ベースのMERGEを使用します。

最終的に、アプリケーションとは切り離されたデータモデルの周りにハックを設計しています。レガシーコードで使用されるテーブルの形状を完全に変更するためのINSTEAD OFトリガを作成することは、これまでのところうまくいきます。この問題は、多くの問題の1つにすぎません。最終的には、クライアントコードが正しい表を挿入/更新/削除する必要があります。あなたがキャプチャするトリガー両方貿易TradePendingとビューとして古い貿易とTradePendingテーブルを公開し、両者を区別するために、状態列を使用し、使用を保持するテーブルにすべてのデータを移動してみてください可能性が回避策として

ビューのDMLを適切な表にリダイレクトします。もしうまくいくかどうかわからないが、今はそれをテストすることはできない。

アップデート:TradePendingにあるレコードに対して取引を更新するために、まだ可能ではありません

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 

注:

ここでは、これは更新可能なビューでどのように動作するかの例です。トリガー、ビューまたは類似のメカニズムはそのようなことを行うことはできません。

+0

分析していただきありがとうございます。質問されていなかった質問に「これは奇妙なアイデアですか?」と思われます。 "あなたはTradePendingにある行のUPDATE文を発行します。 "アプリケーションからTradePendingテーブルを隠そうとしていました。私はUPDATE文を傍受して、TradePendingテーブルに変更を適用できると思っていました。(たぶん間違っているでしょう) – Tony

+0

ありがとうあなたの助けのための利益、そして更新された答え。保留中の取引を更新する方法がない場合は、解決策を再考する必要があります。私は、データベースへのサービス書き込みに大きな変更を加えることを避けようとしていました。 – Tony

0

私はこれを実行する時間がありませんでしたが、挿入されたテーブルが空であることは確かですか? (あなたはいつも他のテーブルに参加しているので、それらのテーブルにレコードがないと結果セットで行が表示されなくなる可能性があります)。更新のためには、挿入されたセットと削除されたセットが必要です。もちろん

+0

IF EXISTSチェックの結果に応じてTradeまたはTradePendingテーブルに参加します。これは、アクセスするためにINSERTEDテーブルに参加しなければならないことを理解しているからです。理論私は "SELECT COUNT(*)FROM inserted"という印を付けました。count = 0。削除されたcountと同じです。 – Tony

+0

Ok、私はそれにアクセスするためにINSERTEDに参加する必要があるのは間違っています。私はあることをTradePendingテーブルにUPDATEを変更:「UPDATE TradePending \tは、SET BUYSELL = とパーサはそれに満足しているが、それは実行時に私が取得(SELECT BUYSELL inserted.HoldingID = TradePending.HoldingID挿入 \t \tより) "BuySellの列に値NULLを挿入できません" – Tony

関連する問題