Выходное предложение VS триггеров

В нашей базе данных большинство таблиц имеют dbupddate поле, которое указывает на datetime из последних INSERT или же UPDATE применяется в ряду.

Чтобы это поле не имело ошибочного значения, существуют триггеры (иногда AFTERиногда INSTEAD OF), которые в конечном итоге обеспечивают правильность значения, а не "ручного" другого значения, которое кто-либо может попытаться записать в это поле.

Сейчас я выполняю заявление об обновлении (на самом деле MERGE) и я хочу иметь OUTPUT пункт, включающий это поле. Как я прочитал в соответствующей статье MS, OUTPUT игнорирует триггеры.

Есть ли обходной путь, чтобы иметь OUTPUT вернуть значение dbupddate после триггеров? Я не хочу делать еще один запрос для извлечения информации, потому что я не гарантирую, что за долю секунды между этими запросами третий запрос другого пользователя мог не изменить совсем ничего.


Результаты после следования советам Ларну

Я запустил предоставленные примеры, за единственным исключением изменения default значения updatetime поля для convert(datetime2,'1900-01-01') так что я мог бы иметь некоторый смысл. Я выполнил каждый из 4 запросов, затем выбрал их из соответствующей таблицы и сравнил updatetime ценности:

INSERT INTO dbo.Sample1 (Someint)
OUTPUT inserted.*
INTO @inserted
SELECT 1;

SELECT 'Sample1 INSERT',*
FROM @inserted; -- 1900-01-01 00:00:00.000000
select * from Sample1  -- 2018-11-05 13:12:13.141580

Я предполагаю, что вывод здесь игнорирует триггер и возвращает значение по умолчанию, которое было вставлено до after триггер вступил в силу.

DECLARE @inserted table (ID int, Someint int, updatedtime datetime2(6))
INSERT INTO dbo.Sample2 (Someint)
OUTPUT inserted.*
INTO @inserted
SELECT 1;

SELECT 'Sample2 INSERT',* --1900-01-01 00:00:00.000000
FROM @inserted;
select * from Sample2 --2018-11-05 13:12:35.580190

Так же. Теперь приходит сумасшедшая часть. Я нарисовал как вставленные, так и удаленные даты:

DECLARE @updated table (ID int, Someint int, ins_updatedtime datetime2(6),del_updatedtime datetime2(6))
UPDATE dbo.Sample1 
SET Someint = 2
OUTPUT Inserted.*,Deleted.updatetime
INTO @updated;

SELECT 'Sample1 UPDATE',*
FROM @updated;   --Sample1 UPDATE   1   2   2018-11-05 13:30:01.348490  2018-11-05 13:30:01.348490
select * from Sample1  -- 1 2   2018-11-05 13:31:31.851047


DECLARE @updated table (ID int, Someint int, ins_updatedtime datetime2(6),del_updatedtime datetime2(6))
UPDATE dbo.Sample2
SET Someint = 2
OUTPUT Inserted.*,Deleted.updatetime
INTO @updated;

SELECT 'Sample2 UPDATE',* -- Sample2 UPDATE 1   2   2018-11-05 13:30:20.286422  2018-11-05 13:30:20.286422
FROM @updated;
select * from Sample2 --1   2   2018-11-05 13:31:51.679726

Итак, в update В некоторых случаях значение по умолчанию отсутствует, но у меня есть разные значения в фактической таблице и в выводе запроса. Я не знаю ни как сделать эти значения одинаковыми, ни что именно происходит с datetime в случае обновления.

1 ответ

Ты можешь использовать OUTPUT с TRIGGER но вы также должны использовать INTO пункт также. Возьмите эти примеры таблиц и триггеров:

CREATE TABLE dbo.Sample1 (SomeID int IDENTITY(1,1),
                          Someint int,
                          updatetime datetime2(6) DEFAULT SYSDATETIME());
CREATE TABLE dbo.Sample2 (SomeID int IDENTITY(1,1),
                          Someint int,
                          updatetime datetime2(6) DEFAULT SYSDATETIME());

GO

CREATE TRIGGER dbo.AfterInsertUdpate ON dbo.Sample1
AFTER INSERT, UPDATE
AS
    UPDATE S
    SET S.updatetime = SYSDATETIME()
    FROM dbo.Sample1 S
         JOIN Inserted i ON S.SomeID = i.SomeID;
GO

CREATE TRIGGER dbo.InsteadInsert ON dbo.Sample2
INSTEAD OF INSERT
AS
    INSERT INTO dbo.Sample2 (Someint,
                             updatetime)
    SELECT Someint, SYSDATETIME()
    FROM Inserted;

GO

CREATE TRIGGER dbo.InsteadUpdate ON dbo.Sample2
INSTEAD OF UPDATE
AS
    UPDATE S
    SET S.Someint = i.Someint,
        S.updatetime = SYSDATETIME()
    FROM dbo.Sample2 S
         JOIN Inserted i ON S.SomeID = i.SomeID;

Если бы мы запустили следующий SQL, вы бы получили ошибку:

INSERT INTO dbo.Sample1 (Someint)
OUTPUT inserted.*
SELECT 1;

Сообщение 334, Уровень 16, Состояние 1, Строка 44 В целевой таблице 'dbo.Sample1' оператора DML не может быть никаких включенных триггеров, если оператор содержит предложение OUTPUT без предложения INTO.

Ошибка дает вам подсказку здесь, используйте INTO пункт. Таким образом, вы можете вместо этого сделать:

DECLARE @inserted table (ID int, Someint int, updatedtime datetime2(6))

INSERT INTO dbo.Sample1 (Someint)
OUTPUT inserted.*
INTO @inserted
SELECT 1;

SELECT 'Sample1 INSERT',*
FROM @inserted;

Это работает для обоих INSERT а также UPDATEнезависимо от того, если это AFTER или же INSTEAD OF:

DECLARE @inserted table (ID int, Someint int, updatedtime datetime2(6))
INSERT INTO dbo.Sample2 (Someint)
OUTPUT inserted.*
INTO @inserted
SELECT 1;

SELECT 'Sample2 INSERT',*
FROM @inserted;

GO
DECLARE @updated table (ID int, Someint int, updatedtime datetime2(6))
UPDATE dbo.Sample1 
SET Someint = 2
OUTPUT Inserted.*
INTO @updated;

SELECT 'Sample1 UPDATE',*
FROM @updated;

GO
DECLARE @updated table (ID int, Someint int, updatedtime datetime2(6))
UPDATE dbo.Sample2
SET Someint = 2
OUTPUT Inserted.*
INTO @updated;

SELECT 'Sample2 UPDATE',*
FROM @updated;
GO

--Clean up
DROP TABLE dbo.Sample1;
DROP TABLE dbo.Sample2;
Другие вопросы по тегам