【问题标题】:Maintain log using trigger for update使用触发器维护日志以进行更新
【发布时间】:2019-11-11 10:31:13
【问题描述】:

我创建了以下数据库、表和更新触发器以维护更新日志。当用户修改 name/salary/gender/departmentid 时,它会更新 'tblEmployeeAudit' 表中的日志。

现在有一个问题,如果我们只是更新系统中已经更新的相同值,那么 'tblEmployeeAudit' 表中不应该更新更新条目。

例如:- 如果 tblEmployee 的 EmployeeName Ravi 具有 Id=1 则如果更新相同的名称,则不应更新日志,但在此不会发生。请帮帮我。

Create database learning
use learning

USE [learning]
GO

/****** Object:  Table [dbo].[tblEmployee]    Script Date: 11/11/2019 3:54:13 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[tblEmployee](
    [Id] [int] NOT NULL,
    [Name] [nvarchar](30) NULL,
    [Salary] [int] NULL,
    [Gender] [nvarchar](10) NULL,
    [DepartmentId] [int] NULL,
PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO


USE [learning]
GO

/****** Object:  Table [dbo].[tblEmployeeAudit]    Script Date: 11/11/2019 3:54:34 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[tblEmployeeAudit](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [AuditData] [nvarchar](1000) NULL,
PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

Alter trigger tr_tblEmployee_ForUpdate
on tblEmployee
For Update
As
Begin
    Declare @Id int
    Declare @OldName nvarchar(30),@NewName nvarchar(30)
    Declare @OldSalary int,@NewSalary int
    Declare @OldGender nvarchar(10),@NewGender nvarchar(10)
    Declare @OldDeptId int,@NewDeptId int

    Declare @AuditString nvarchar(1000)

    Select * into #TempTable
    from inserted

    While(Exists(Select Id from #TempTable))
    Begin
        Set @AuditString=''

        Select Top 1 @Id=Id,@NewName=Name,@NewSalary=Salary,@NewGender=Gender,@NewDeptId=DepartmentId from #TempTable

        Select @OldName=Name,@OldSalary=Salary,@OldGender=Gender,@OldDeptId=DepartmentId from deleted where Id=@Id

        Set @AuditString='Employee with Id='+CAST(@Id as nvarchar(4))+' changed '
        if(@OldName<>@NewName)
            Set @AuditString=@AuditString+'NAME from '+@OldName+' to '+@NewName

        if(@OldSalary<>@NewSalary)
            Set @AuditString=@AuditString+'Salary from '+@OldSalary+' to '+@NewSalary

        if(@OldGender<>@NewGender)
            Set @AuditString=@AuditString+'Gender from '+@OldGender+' to '+@NewGender

        if(@OldDeptId<>@NewDeptId)
            Set @AuditString=@AuditString+'DepartmentId from '+@OldDeptId+' to '+@NewDeptId

        insert into tblEmployeeAudit values(@AuditString)
        Delete from #TempTable where Id=@Id

    End

End



   update tblEmployee
    set Name='Joseph' where Id=4

注意:- 即使没有不匹配的行更新,也跟随插入的数据 Id=4 的员工已更改

【问题讨论】:

  • 您使用的是什么版本的 SQL Server?如果是 2016 年或更高版本,您可能应该检查 system-versioned-tables 而不是为此使用触发器。
  • 主要原因是以下查询导致插入默认行,如果执行任何更新查询。 Set @AuditString='Employee with Id='+CAST(@Id as nvarchar(4))+' changed '
  • 我使用的是SQL server 2014版本,我相信版本不会影响这个
  • 只需要检查新旧值是否不匹配,然后再将记录插入 tblEmployeeAudit,如果是,则将记录插入 tblEmployeeAudit 否则无操作

标签: sql-server tsql database-trigger


【解决方案1】:

不要创建变量来存储更改的值,而是使用插入和删除的表来检查值是否已更改,我认为这将解决您的问题

从插入的 i 中选择 * select * from deleted d

在 * 附近,您只能选择要检查其值的列名。

【讨论】:

    【解决方案2】:

    我建议您在生成 AuditString 之前先检查是否有任何更改。您可以使用 EXCEPT 运算符来检查更改。

    IF EXISTS ( SELECT Id, Name, Salary, Gender from inserted 
                EXCEPT
                SELECT Id, Name, Salary, Gender from deleted)
    BEGIN
    -- GO FOR AUDIT LOGIC
    END
    

    Read more on EXCEPT

    【讨论】:

      猜你喜欢
      • 2010-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-19
      • 1970-01-01
      • 2021-11-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多