无光标触发批量更新

发布于 2025-01-02 00:47:28 字数 2361 浏览 3 评论 0原文

我在 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.

如何更改当前触发器以进行批量更新。我必须使用光标来执行此操作吗?如果是这样,那么有人可以提供一些示例代码吗?

I have a trigger in SQL Server 2005 that is used to track audit changes on specific fields. We track the changes in a separate table. This trigger works as expected for the single record entries which is how it was intended but now they want to track changes made to the column via a bulk update.

Current trigger:

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

This code doesn't work for multiple records it throws an error:

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

How can I alter my current trigger to work for a bulk update. Do I have to use a cursor to do it? If so, then can someone offer some sample code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

初见 2025-01-09 00:47:28

当有多个记录时,如何设置@UpdatedDate和@UpdatedBy?我怀疑你正在做类似的事情,这就是你为一张记录所做的事情。

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

如果 INSERTED 中有多于一行,则 ELSE 下的 SET 将导致错误。

试试这个

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 没有更改,则不会返回任何记录。

How are you setting @UpdatedDate and @UpdatedBy when there is more than one record? I suspect you are doing something like this, which is what you are doing for one record.

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

The SET under ELSE will cause the error if there is more than one row in INSERTED.

Try this instead

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)

This should work for any number of records, including just one. Note that I am not checking UPDATE(ActDate). No records will be returned if ActDate did not change.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文