У меня были серьезные сомнения по поводу использования @@identity, потому что это может вернуть неверный ответ.
Но есть обходной путь, чтобы заставить @@identity иметь значение scope_identity().
Просто для полноты сначала я перечислю пару других обходных путей для этой проблемы, которые я видел в Интернете:
Заставьте триггер возвращать набор строк. Затем в оболочке SP, выполняющей вставку, выполните INSERT Table1 EXEC sp_ExecuteSQL ...
еще одну таблицу. Тогда будет работать scope_identity(). Это грязно, потому что требует динамического SQL, что является проблемой. Кроме того, имейте в виду, что динамический SQL выполняется с разрешениями пользователя, вызывающего SP, а не с разрешениями владельца SP. Если первоначальный клиент мог вставлять в таблицу, он все еще должен иметь это разрешение, просто знайте, что вы можете столкнуться с проблемами, если откажете в разрешении на вставку непосредственно в таблицу.
Если есть другой ключ-кандидат, получите идентификатор вставленных строк, используя эти ключи. Например, если имя имеет уникальный индекс, вы можете вставить, а затем выбрать идентификатор (максимум для нескольких строк) из таблицы, которую вы только что вставили, используя имя. Хотя это может привести к проблемам параллелизма, если другой сеанс удалит только что вставленную строку, это не хуже, чем в исходной ситуации, если кто-то удалит вашу строку до того, как приложение сможет ее использовать.
Теперь, вот как окончательно сделать ваш триггер безопасным, чтобы @@Identity возвращал правильное значение, даже если ваш SP или другой триггер выполняет вставку в таблицу, содержащую идентификационные данные, после основной вставки.
Кроме того, пожалуйста, добавляйте в свой код комментарии о том, что вы делаете и почему, чтобы будущие посетители триггера не ломали вещи и не тратили время, пытаясь понять это.
CREATE TRIGGER TR_MyTable_I ON MyTable INSTEAD OF INSERT
AS
SET NOCOUNT ON
DECLARE @MyTableID int
INSERT MyTable (Name, SystemUser)
SELECT I.Name, System_User
FROM Inserted
SET @MyTableID = Scope_Identity()
INSERT AuditTable (SystemUser, Notes)
SELECT SystemUser, 'Added Name ' + I.Name
FROM Inserted
-- The following statement MUST be last in this trigger. It resets @@Identity
-- to be the same as the earlier Scope_Identity() value.
SELECT MyTableID INTO #Trash FROM MyTable WHERE MyTableID = @MyTableID
Обычно дополнительная вставка в таблицу аудита нарушает все, потому что, поскольку в ней есть столбец идентификаторов, @@Identity вернет это значение, а не значение из вставки в MyTable. Однако окончательный выбор создает новое значение @@Identity, которое является правильным, на основе Scope_Identity(), которую мы сохранили ранее. Это также защищает его от любого возможного дополнительного триггера AFTER в таблице MyTable.
Обновление:
Я только что заметил, что триггер INSTEAD OF здесь не нужен. Это делает все, что вы искали:
CREATE TRIGGER dbo.TR_Payments_Insert ON dbo.Payment FOR INSERT
AS
SET NOCOUNT ON;
IF EXISTS (
SELECT *
FROM
Inserted I
INNER JOIN dbo.Payment P ON I.CustomerID = P.CustomerID
WHERE
I.DateFrom < P.DateTo
AND P.DateFrom < I.DateTo
) ROLLBACK TRAN;
Это, конечно, позволяет продолжать работу scope_identity(). Единственным недостатком является то, что вставка с откатом в таблицу идентификаторов использует используемые значения идентификатора (значение идентификатора по-прежнему увеличивается на количество строк в попытке вставки).
Я смотрю на это уже несколько минут и сейчас не могу быть абсолютно уверен, но я думаю, что это сохраняет значение инклюзивного времени начала и исключительного времени окончания. Если бы время окончания было инклюзивным (что было бы странно для меня), тогда при сравнении нужно было бы использовать ‹= вместо ‹.
person
ErikE
schedule
06.03.2010