AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 46672
Accepted
Hani El Mouallem
Hani El Mouallem
Asked: 2013-07-20 03:56:18 +0800 CST2013-07-20 03:56:18 +0800 CST 2013-07-20 03:56:18 +0800 CST

处理多行更新的更新后触发器

  • 772

目前正在开发一个数据库审计项目,该项目基于在特定表上更新时触发的触发器。触发器将更改写入表;写入的信息有:表名、更新列、时间戳、用户、旧值和新值。

触发器在单次更新时工作正常,但在多行更新时,它不起作用。

我的代码是这样的:

IF (UPDATE(Priority))  
BEGIN
    SET @UpdatedColumn = 'Priority'
    INSERT INTO dbo.AuditTable
        ( [TableName] ,
          [Source] ,
          [RecordId] ,
          [User] ,
          [TimeStamp] ,
          [UpdatedColumn] ,
          [OldValue] ,
          [NewValue]
        )
    SELECT 
        N'BookingItem' , -- TableName - nvarchar(max)
        (SELECT CODE FROM TBL_LEG_SOURCE 
                     INNER JOIN INSERTED INS ON LEG_SOURCE_ID = INS.SourceId) ,
        INS.Id , -- RecordId - bigint
        (SELECT USERNAME FROM INSERTED 
                     INNER JOIN TBL_USER 
                     ON ModifiedById = USER_ID) , -- User - nvarchar(max)
        GETDATE() , -- TimeStamp - datetime
        @UpdatedColumn , -- UpdatedColumn - nvarchar(max)
        DEL.Priority , -- OldValue - nvarchar(max)
        INS.Priority  -- NewValue - nvarchar(max)
    FROM 
        INSERTED INS INNER JOIN DELETED DEL ON INS.Id = DEL.Id
    WHERE
        (
            (INS.Priority <> DEL.Priority)
            OR (INS.Priority IS NULL AND DEL.Priority IS NOT NULL)
            OR (INS.Priority IS NOT NULL AND DEL.Priority IS NULL)
        )
END

错误信息:

消息 512,级别 16,状态 1,过程 MyTrigger,第 818 行
子查询返回超过 1 个值。当子查询跟随 =、!=、<、<=、>、>= 或子查询用作表达式时,这是不允许的。

有关如何修复触发器以处理多行操作的任何建议?

sql-server sql-server-2012
  • 2 2 个回答
  • 16859 Views

2 个回答

  • Voted
  1. Aaron Bertrand
    2013-07-20T06:21:51+08:002013-07-20T06:21:51+08:00

    以下是如何使用正确的连接来修复错误(如果这还不够“快”,请查看您的索引):

    INSERT dbo.AuditTable
    (
      [TableName],
      [Source],
      [RecordId],
      [User],
      [TimeStamp],
      [UpdatedColumn],
      [OldValue],
      [NewValue]
    )
    SELECT 
      N'BookingItem', -- TableName - nvarchar(max)
      ls.CODE,
      INS.Id, -- RecordId - bigint
      u.USERNAME,
      GETDATE(), -- TimeStamp - datetime
      @UpdatedColumn, -- UpdatedColumn - nvarchar(max)
      DEL.Priority, -- OldValue - nvarchar(max)
      INS.Priority  -- NewValue - nvarchar(max)
    FROM 
      INSERTED AS INS 
    INNER JOIN 
      DELETED AS DEL ON INS.Id = DEL.Id
    INNER JOIN 
      dbo.TBL_LEG_SOURCE AS ls ON ls.LEG_SOURCE_ID = INS.SourceId
    INNER JOIN
      dbo.TBL_USER AS u ON INS.ModifiedById = u.USER_ID
    WHERE
    (
      (INS.Priority <> DEL.Priority)
      OR (INS.Priority IS NULL AND DEL.Priority IS NOT NULL)
      OR (INS.Priority IS NOT NULL AND DEL.Priority IS NULL)
    );
    

    但是我认为运行 50 多个不同的插入来捕获每一个列的变化是非常愚蠢的。为什么不直接创建一个包含时间和表名列的表(您不需要存储用户名,因为您以后可以随时查看),然后每当有更新时,存储该行的旧版本和新版本?您甚至可以使用 aSEQUENCE来确保您可以识别一起修改的行集(因为时间戳可能不够独特,无法做到这一点)。

    CREATE SEQUENCE dbo.AuditSequence
      AS INT START WITH 1 INCREMENT BY 1;
    
    CREATE TABLE dbo.AuditData
    (
      AuditSequenceID INT,
      TableName SYSNAME,
      [TimeStamp] DATETIME,
      RowState CHAR(1), -- e.g. 'B' = before, 'A' = after
      ... all your 50 columns, including ModifidById ...
    );
    

    现在在您的触发器中:

    CREATE TRIGGER dbo.MyTrigger
      ON dbo.BookingItem
      FOR UPDATE
    AS
    BEGIN
      SET NOCOUNT ON;
    
      DECLARE @as INT = NEXT VALUE FOR dbo.AuditSequence,
              @now DATETIME = CURRENT_TIMESTAMP;
    
      INSERT dbo.AuditData(AuditSequenceID, TableName, [TimeStamp], RowState,
        ... the rest of your 50 columns)
      SELECT @as, N'BookingItem', @now, 'B', * FROM deleted;
    
      INSERT dbo.AuditData(AuditSequenceID, TableName, [TimeStamp], RowState,
        ... the rest of your 50 columns)
      SELECT @as, N'BookingItem', @now, 'A', * FROM inserted;
    END
    GO
    

    现在,针对这种效率低下的更简单的审计结构编写复杂的查询,并尝试准确跟踪哪些列已更改以及所有这些。在审查审计数据时付出这个代价要比在每次更新操作上付出那个代价要好得多。

    • 9
  2. Best Answer
    Sebastian Meine
    2013-07-20T06:24:31+08:002013-07-20T06:24:31+08:00

    您显示的查询中的两个子查询都连接到 INSERTED,而无需执行聚合或 top(1)。所以两者都可能返回不止一行。无需再次加入 INSERTED 表,只需直接引用该列即可。这样,第二个查询将如下所示:

    (SELECT U.USERNAME FROM TBL_USER U WHERE INS.ModifiedById = U.USER_ID)
    

    另一个的变化是类似的。

    • 4

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    授予用户对所有表的访问权限

    • 5 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    pedrosanta 使用 psql 列出数据库权限 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve