简体   繁体   English

不使用光标更新触发器

[英]Update Trigger without using a Cursor

I need to create a trigger that will add a record to a queue table whenever a record is modified in the main table. 我需要创建一个触发器,只要在主表中修改记录,就会将记录添加到队列表中。 The record added to the queue table must contain every field that was modified for that record. 添加到队列表的记录必须包含为该记录修改的每个字段。

I have this code so far but I don't think it will work for multiple rows updated: 到目前为止我有这个代码,但我不认为它会更新多行:

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 

If I am understanding this correctly, if one record had last name updated and another record got the first name updated at the same time then both records would be logged as having both first and last named changed. 如果我正确理解这一点,如果一条记录的姓氏已更新,另一条记录同时更新了第一个名称,则两条记录都将被记录为同时更改了第一个和最后一个名称。 Is that correct? 那是对的吗? If so, how could I make it work correctly without using a cursor? 如果是这样,如何在不使用游标的情况下使其正常工作?

The only way I can think to solve the problem is to use a cursor but I know that is not a good idea. 我能想到解决问题的唯一方法是使用光标,但我知道这不是一个好主意。 Any help would be appreciated. 任何帮助,将不胜感激。

Thanks! 谢谢!

Your code as it stands will not work if multiple records are updated. 如果更新了多个记录,您的代码将无法正常工作。

Are you really tied to that audit table structure? 你真的与审计表结构挂钩吗? We find it more useful to store both the old and new data as well as the id of the application or person who changed the data and the date of the data. 我们发现存储旧数据和新数据以及应用程序的ID或更改数据的人员以及数据的日期更有用。 This structure will make it very hard to retrieve data back if someone makes a change you want to reverse and doesn't allow you to identify when or who made the change. 如果有人进行了您想要撤消的更改,并且不允许您确定何时或谁进行更改,则此结构将很难检索数据。 If you haven't yet implemeted the audit tables I would first seriosuly consider redesigning them. 如果您还没有实施审计表,我首先会仔细考虑重新设计它们。

If I was stuck with the design you show, I would create temp tables or table variables to store the data as you go through each field. 如果我坚持你展示的设计,我会创建临时表或表变量来存储每个字段的数据。 I woudl use an isnert for teh first one and then a merge statement for each of the others to either update the record if is is already tehre or insert a new one if it is not. 我首先使用一个isnert用于第一个,然后为每个其他用户使用合并语句,如果已经是tehre则更新记录,如果不是,则更新新的记录。 Once the temp table is populated, then I woudl insert to the audit table witha selct from that table. 填充临时表后,我将从该表中选择插入到审计表中的selct。 This will be faster than a cursor, but not speedy due to the really bad design of the audit table. 这将比游标更快,但由于审计表的设计非常糟糕,因此速度不快。 One should not generally design a table to include comma delimited lists espcially not when you are going to need performance on the query as you will in a trigger. 通常不应该设计一个表来包含逗号分隔列表,特别是当您需要在查询中执行性能时,就像在触发器中一样。

You can use CASE to build a value row-by-row showing the changed columns: 您可以使用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

Some additional fiddling about can eliminate the extra delimiter. 一些额外的摆弄可以消除额外的分隔符。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM