Почему таблица INSERTED триггера INSTEAD OF UPDATE пуста? - PullRequest
3 голосов
/ 02 декабря 2010

План: Использование триггера INSTEAD OF INSERT для перенаправления неудачных вставок в таблицу «ожидания».Эти строки остаются в «ожидающей» таблице, пока некоторая дополнительная информация не будет вставлена ​​в другую таблицу;когда эта новая информация становится доступной, ожидающие строки перемещаются в исходное место назначения.

Справочная информация: Сделки регистрируются относительно Холдинга.Служба, обновляющая Сделки, может иметь информацию, которой еще нет в базе данных, такую ​​как сделка по Холдингу, которая еще не была добавлена ​​(пожалуйста, не сосредотачивайтесь на «почему» этой части системы, я могу »изменить это).

Проблема: Триггер 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 пуста?Я уверен, что решение будет чем-то тривиальным, но я просто не могу заставить его работать.

Ответы [ 2 ]

3 голосов
/ 02 декабря 2010

Конечно, строки не существуют в псевдотесте INSERTED, когда вы обновляете строки, которых не существует в таблице , чтобы начать с: вы выполняете инструкцию UPDATE на Trade для строк, которыенаходятся в TradePending!

Кроме того, ваш триггер INSTEAD OF INSERT не работает.Это работает только для однорядных вставок, и даже для тех, которые будут терпеть неудачу при параллельности.Используйте MERGE на основе множеств.

В конечном итоге вы разрабатываете хак для модели данных, которая не связана с тем, что делает приложение.Создание триггеров INSTEAD OF для полного изменения формы таблицы, используемой унаследованным кодом, пока работает, эта проблема, с которой вы столкнулись, является лишь одной из многих проблем в будущем.В конечном счете, ваш клиентский код должен вставить / обновить / удалить нужную таблицу.

В качестве обходного пути вы можете попробовать переместить все данные в таблицу, которая содержит и Trade и TradePending и использует состояниеВ столбце, чтобы различить эти два, представьте старые таблицы Trade и 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

Обратите внимание, что по-прежнему невозможно обновить Trade для записей, находящихся в TradePending.Никакой триггер, представление или подобный механизм не могут сделать это.

0 голосов
/ 02 декабря 2010

У меня не было времени, чтобы запустить это, но вы уверены, что вставленная таблица пуста?(Вы всегда присоединяетесь к другим таблицам, поэтому отсутствие записей в этих таблицах может привести к подавлению строки в ваших результирующих наборах.) Как насчет удаленных?Для обновления у вас должен быть вставленный и удаленный набор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...