【问题标题】:Update Trigger without using a Cursor不使用光标更新触发器
【发布时间】:2012-08-30 14:07:23
【问题描述】:

我需要创建一个触发器,该触发器将在主表中的记录被修改时将记录添加到队列表中。添加到队列表的记录必须包含为该记录修改的每个字段。

到目前为止,我有这段代码,但我认为它不适用于更新的多行:

ALTER TRIGGER [dbo].[tr_EmpHistory]    
ON [dbo].[employeeData]  
FOR UPDATE 
AS 
BEGIN      
DECLARE @FieldsUpdated xml,  
@FieldsUpdated1 varchar(100)       
SELECT @Fieldsupdated1 = ' ' 
SELECT 
@FieldsUpdated1 = @FieldsUpdated1 + ' emp_bankaccountnumber' 
FROM inserted as a,  
deleted as b  
WHERE a.emp_id = b.emp_id             
AND a.emp_bankAccountNumber <> b.emp_bankAccountNumber  
SELECT      
@FieldsUpdated1 = @FieldsUpdated1 + 'emp_salary ' 
FROM inserted as a,        
deleted as b   
WHERE a.emp_id = b.emp_id       
AND a.emp_salary <> b.emp_salary 

SELECT    
@FieldsUpdated1 = @FieldsUpdated1 + 'emp_SSN ' 
FROM inserted as a,  
deleted as b  
WHERE a.emp_id = b.emp_id            
AND a.emp_SSN <> b.emp_SSN  
SELECT 
@FieldsUpdated1 = @FieldsUpdated1 + 'emp_lname ' 
FROM inserted as a,  
deleted as b  
WHERE a.emp_id = b.emp_id           
AND a.emp_lname <> b.emp_lname  
SELECT      
@FieldsUpdated1 = @FieldsUpdated1 + 'emp_fname ' 
FROM inserted as a,  
deleted as b  
WHERE a.emp_id = b.emp_id     
AND a.emp_fname <> b.emp_fname  
SELECT 
@FieldsUpdated1 = @FieldsUpdated1 + 'emp_manager ' 
FROM inserted as a,  
deleted as b  
WHERE a.emp_id = b.emp_id          
AND a.emp_manager <> b.emp_manager  
SELECT @Fieldsupdated =  ( 
SELECT COLUMN_NAME AS Name 
FROM INFORMATION_SCHEMA.COLUMNS  
WHERE TABLE_NAME = 'employeeData' 
AND CHARINDEX(COLUMN_NAME,(ltrim(rtrim(@fieldsupdated1)))) > 0 
FOR XML AUTO, ROOT('Fields') )

INSERT INTO auditEmployeeData( 
audit_emp_id, 
audit_emp_bankAccountNumber,
audit_emp_salary, 
audit_emp_SSN, 
audit_emp_lname, 
audit_emp_fname, 
audit_emp_manager, 
ColumnsUpdated ) 

SELECT emp_id, 
emp_bankAccountNumber, 
emp_salary,    
emp_SSN,   
emp_lname,  
emp_fname,     
emp_manager,       
@FieldsUpdated   
FROM INSERTED  
END
GO 

如果我的理解正确,如果一条记录更新了姓氏,而另一条记录同时更新了名字,那么这两条记录将被记录为名字和姓氏都发生了变化。那是对的吗?如果是这样,如何在不使用光标的情况下使其正常工作?

我认为解决问题的唯一方法是使用光标,但我知道这不是一个好主意。任何帮助将不胜感激。

谢谢!

【问题讨论】:

  • 请不要使用隐含的连接语法,这是一种 SQL 反模式!

标签: sql sql-server triggers cursor


【解决方案1】:

如果更新了多条记录,您的代码将无法正常工作。

你真的与那个审计表结构有关吗?我们发现存储旧数据和新数据以及更改数据的应用程序或人员的 ID 和数据日期更有用。如果有人进行了您想要撤消的更改并且不允许您识别何时或谁进行了更改,这种结构将使检索数据变得非常困难。如果您还没有实现审计表,我会首先认真考虑重新设计它们。

如果我坚持您展示的设计,我会在您浏览每个字段时创建临时表或表变量来存储数据。我会为第一个使用一个 isert,然后为其他每个使用一个合并语句来更新记录,如果它已经是 tehre,或者插入一个新的,如果不是。填充临时表后,我将使用该表中的选择插入审计表。这将比游标快,但由于审计表的设计非常糟糕,所以速度不快。通常不应将表设计为包含逗号分隔的列表,尤其是当您需要像在触发器中那样对查询进行性能时。

【讨论】:

  • 它不适用于审计表,并且没有任何内容存储在逗号分隔的列表中。每当我们的数据更改以更新第 3 方时,我们都必须调用第 3 方 Web 服务。此队列表将存储更新的记录以及在 XML 中更改的特定字段。服务将运行此队列表并获取任何更改字段的当前值并调用第 3 方 Web 服务以通知他们新的更改数据。
  • auditEmployeeData 不是审计表?请原谅我这么认为。
  • 我不需要知道旧值是什么或谁/什么改变了数据。我只关心数据是否真的被改变了。
【解决方案2】:

您可以使用CASE 逐行构建一个值来显示更改的列:

select i.emp_id,
  case when i.foo <> d.foo then ',foo' else '' end +
  case when i.bar <> d.bar then ',bar' else '' end as changedcolumns
  from inserted as i inner join
    deleted as d on d.emp_id = i.emp_id

一些额外的摆弄可以消除额外的分隔符。

【讨论】:

  • 我认为这可能有效。让我做一些测试以确保。谢谢!
  • 这正是我所需要的。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-27
  • 2012-07-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多