Массовое обновление триггера без курсора - PullRequest
1 голос
/ 02 февраля 2012

У меня есть триггер в SQL Server 2005, который используется для отслеживания изменений аудита в определенных полях. Мы отслеживаем изменения в отдельной таблице. Этот триггер работает, как и ожидалось, для отдельных записей записей, как и было задумано, но теперь они хотят отслеживать изменения, внесенные в столбец с помощью массового обновления.

Текущий триггер:

CREATE TRIGGER [dbo].[trg_LogChanges]
   ON [dbo].[Test]
   FOR UPDATE
AS 

DECLARE @TableName VARCHAR(100) ,
    @UpdatedDate smalldatetime ,
    @UpdatedBy uniqueidentifier

SELECT @TableName = 'dbo.Test'

IF(SELECT COUNT(*) FROM INSERTED) = 1
    BEGIN
       IF(SELECT LastModifiedDate FROM INSERTED) Is Null
        SET @UpdatedDate = getdate()
       ELSE
        SET @UpdatedDate = (SELECT LastModifiedDate FROM INSERTED)

       IF(SELECT LastModifiedBy FROM INSERTED) Is Null
        SET @UpdatedBy = '11111111-1111-1111-1111-111111111111'
       ELSE
        SET @UpdatedBy = (SELECT LastModifiedBy FROM INSERTED)

            IF UPDATE (ActDate)
        BEGIN
        INSERT INTO dbo.LogChanges
        (
            ChangeType
            , TableName
            , RecordGuid
            , FieldName
            , OldValue
            , NewValue
            , UpdatedBy
            , UpdatedDate
        )
        SELECT 
            'U'
            , @TableName
            , d.Guid
            , 'ActDate'
            , d.ActDate
            , i.ActDate
            , @UpdatedBy
            , @UpdatedDate
        FROM INSERTED i
        INNER JOIN DELETED d
            on i.Guid = d.Guid
        WHERE 
            (d.ActDate IS NULL AND i.ActDate IS NOT NULL)
            OR (d.ActDate IS NOT NULL AND i.ActDate IS NULL)
            OR (d.ActDate <> i.ActDate)
       END
        -- this keeps going for each field that we need to get the Audit Trail on
    END
ELSE
    BEGIN
        -- now I need to track for multiple records
        -- I tried changing the WHERE clause above to see if it would work for bulk updates
        INSERT INTO...
        SELECT...
        WHERE 
    (
        (d.ActDate IS NULL AND i.ActDate IS NOT NULL)
        OR (d.ActDate IS NOT NULL AND i.ActDate IS NULL)
        OR (d.ActDate <> i.ActDate)
    )
    AND d.ActDate IN (SELECT d.ActDate FROM DELETED d)
    END

Этот код не работает для нескольких записей, он выдает ошибку:

Subquery returned more than 1 value. This is not permitted when the subquery 
follows =, !=, <, <= , >, >= or when the subquery is used as an expression.

Как мне изменить текущий триггер, чтобы он работал для массового обновления. Должен ли я использовать курсор, чтобы сделать это? Если да, то может кто-нибудь предложить пример кода?

1 Ответ

2 голосов
/ 02 февраля 2012

Как вы устанавливаете @UpdatedDate и @UpdatedBy, когда существует более одной записи?Я подозреваю, что вы делаете что-то подобное, что вы делаете для одной записи.

IF(SELECT LastModifiedBy FROM INSERTED) Is Null
  SET @UpdatedBy = '11111111-1111-1111-1111-111111111111'
ELSE
 SET @UpdatedBy = (SELECT LastModifiedBy FROM INSERTED)

SET в ELSE вызовет ошибку, если в INSERTED будет более одной строки.

Попробуйте вместо этого

INSERT INTO dbo.LogChanges
(
ChangeType
, TableName
, RecordGuid
, FieldName
, OldValue
, NewValue
, UpdatedBy
, UpdatedDate
)
SELECT 
'U'
, @TableName
, d.Guid
, 'ActDate'
, d.ActDate
, i.ActDate
, ISNULL(LastModifiedBy, '11111111-1111-1111-1111-111111111111')
, ISNULL(LastModifiedDate, getdate())
FROM INSERTED i
INNER JOIN DELETED d
    on i.Guid = d.Guid
WHERE (d.ActDate IS NULL AND i.ActDate IS NOT NULL)
    OR (d.ActDate IS NOT NULL AND i.ActDate IS NULL)
    OR (d.ActDate <> i.ActDate)

Это должно работать для любого количества записей, включая только одну.Обратите внимание, что я не проверяю UPDATE(ActDate).Записи не будут возвращены, если ActDate не изменился.

...