2

I need to compare two row with each other and then write the fields that changed into a table.

My table:

CREATE TABLE dbo.tUserChanges
(
cID int IDENTITY (1,1),
cChangeDate DateTime,
cValueChanged varchar(30),
cPreviousValue bit,
cCurrentValue bit
)

In my C# program the user table needs to be updated when changes are made to the users.

This is done with a stored procedure:

CREATE PROCEDURE [dbo].[UUser]       
( 
 @User as varchar(15),   
 @UCode  AS int,      
 @UName  AS varchar(30),      
 @UID   AS  varchar(10),      
 @UPassword  AS varchar(15),      
 @UPMaintenance as bit,      
 @UClient  as bit,      
 @UFinancial  as bit,      
 @UViewReceiptImage bit,
 @UViewPayrollData bit
) 
AS
BEGIN
UPDATE  tUsers      
 SET        
 UName    = @UName,      
 UID    = @UID,      
 UPassword   = @UPassword,  UPMaintenance   = @UPMaintenance,       
 UClient    = @UClient,      
 UFinancial   = @UFinancial,
 UViewReceiptImage = @UViewReceiptImage,
 UViewPayrollData = @UViewPayrollData     
 WHERE UCode = @UCode 
END 

There is more values in the tUser table but for the sake of keeping it sort I removed some of the values.

So what I need to do is create a temp table at the befor I update the tUsers table so that I can Compare the two rows after the update has been done and then right the changes that took place into the new table.

I have tried this but I know there is a better way and it also does not give the required results:

declare @i int
set @i = 0
declare @ColumnCount int
set @ColumnCount = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE  table_name = @UID)
declare @ColumnName varchar(30)
set @ColumnName = (select column_name from information_schema.columns  where table_name = 'tUsers' and ordinal_position = 14)

select UActivateLoan from tusers

while (@ColumnCount < @i)
Begin
    if((select @ColumnName from tUsers where UID = @UID) 
    <> (select @ColumnName from #myTemp where UID = @UID))
    Begin
        Insert into tUserChanges
        Values(GETDATE(),(select column_name from information_schema.columns  where table_name = 'tUsers' and ordinal_position = 14),
                (select(select column_name from information_schema.columns  where table_name = 'tUsers' and ordinal_position = 14) from #myTemp where UID = @UID),
                (select(select column_name from information_schema.columns  where table_name = 'tUsers' and ordinal_position = 14) from tUsers where UID = @UID))
    END
    set @i = @i + 1
End

I am not sure if I will need to use a cursor here or what I can do to get the result? Any help would be appreciated.

user1171437
  • 25
  • 1
  • 5
  • SQL doesn't magically interpret strings or variables as names. Where you have `select @ColumnName from tUsers where UID = @UID`, what you're saying is "select the value of the `@ColumnName` variable", not "retrieve the column named `@ColumnName` from the table" – Damien_The_Unbeliever Feb 09 '12 at 08:35
  • Yes I know this but I posted this as a example, as I am not sure how to do it – user1171437 Feb 09 '12 at 09:16

1 Answers1

2

This would be better done using an FOR UPDATE trigger.

CREATE TRIGGER dbo.tUser_AfterUpdate ON dbo.tUsers FOR UPDATE AS 
BEGIN
  IF UPDATE(UName) THEN 
    INSERT INTO tUserChanges 
    SELECT GETDATE()
           , (SELECT UName FROM DELETED) -- Old Value
           , (SELECT UName FROM INSERTED) -- New Value

  ...
END

CREATE TRIGGER

Creates a trigger, which is a special kind of stored procedure that executes automatically when a user attempts the specified data-modification statement on the specified table. Microsoft® SQL Server™ allows the creation of multiple triggers for any given INSERT, UPDATE, or DELETE statement.

Edit

Using Dynamic SQL, would be something like

  CREATE TRIGGER dbo.tUser_AfterUpdate ON dbo.tUsers FOR UPDATE AS 
  BEGIN
    DECLARE @Columns TABLE (name sysname)
    DECLARE @ColumnName sysname
    DECLARE @Statement VARCHAR(MAX)

    INSERT INTO @Columns
    SELECT  name
    FROM    sys.columns
    WHERE   OBJECT_NAME(OBJECT_ID) = 'tUsers'

    WHILE EXISTS (SELECT * FROM @Columns)
    BEGIN 
      SELECT TOP 1 @ColumnName = name FROM @Columns
      DELETE FROM @Columns WHERE name = @ColumnName

      SET @Statement = 
        'IF UPDATE(' + @ColumnName + ') THEN '
        + 'INSERT INTO tUserChanges '
        + 'SELECT GETDATE() '
        + '      , (SELECT ' + @ColumnName + 'FROM DELETED) -- Old Value'
        + '      , (SELECT ' + @ColumnName + 'FROM INSERTED) -- New Value'

      EXEC (@Statement)
    END
  END
Lieven Keersmaekers
  • 57,207
  • 13
  • 112
  • 146
  • Then I have to do the update for all the fields in the table there are 216 fields in the table – user1171437 Feb 09 '12 at 09:14
  • As I see it, you have two choices. 1. Copy/Paste the insert statement 216 times, changing the columns. 2. Use dynamic sql. In this case, I have mixed feelings with **both** solutions. It's much easier to get the copy/paste thing right but the dynamic SQL is shorter and adding columns to your base table is automatically covered. – Lieven Keersmaekers Feb 09 '12 at 09:44
  • How would you do the dynamic SQL option – user1171437 Feb 09 '12 at 09:51
  • @user1171437 - I have added a possible example but make sure you test it extensively. I haven't verified if this kind of construction is even possible in an update trigger. – Lieven Keersmaekers Feb 09 '12 at 10:01
  • I get this error when I try to Update just on record in the tUsers table: The name 'IF UPDATE(UAccessAllCompany) THEN INSERT INTO tUserChanges SELECT GETDATE() , (SELECT UAccessAllCompany FROM DELETED) , (SELECT UAccessAllCompany FROM INSERTED)' is not a valid identifier – user1171437 Feb 09 '12 at 12:14
  • @user1171437 - the problem seems to be related to missing parentheses around `@Statement`, I have fixed that. Please note that currently the dynamic statement only includes **three** values to be inserted into `tUserChanges` whereas your actual column defines 5. You'll have to ammend the insertstatement to your actual needs. (I have no idea what to put in the `bit` columns). – Lieven Keersmaekers Feb 09 '12 at 12:52
  • Thanks now I am getting this error: Can only use IF UPDATE within a CREATE TRIGGER statement. – user1171437 Feb 09 '12 at 13:37
  • @user1171437 - That was something I was afraid of. It looks like the dynamic sql solution will not work within a trigger. If using a trigger, you now only have one solution left and that is by writing them all out. – Lieven Keersmaekers Feb 09 '12 at 13:41