it-swarm.com.de

SQL Server-Update-Trigger, Nur geänderte Felder abrufen

COLUMNS_UPDATED ist mir bekannt, und ich brauche eine schnelle Verknüpfung (wenn jemand eine erstellt hat, erstelle ich bereits eine, aber wenn jemand meine Zeit sparen kann, schätze ich sie)

Ich benötige grundsätzlich nur eine XML-Datei mit aktualisierten Spaltenwerten, die ich für Replikationszwecke benötige.

SELECT * FROM eingefügt gibt mir jede Spalte, aber ich brauche nur aktualisierte.

so etwas wie folgendes ...

CREATE TRIGGER DBCustomers_Insert
    ON DBCustomers
    AFTER UPDATE
AS
BEGIN
    DECLARE @sql as NVARCHAR(1024);
    SET @sql = 'SELECT ';


    I NEED HELP FOR FOLLOWING LINE ...., I can manually write every column, but I need 
    an automated routin which can work regardless of column specification
    for each column, if its modified append $sql = ',' + columnname...

    SET @sql = $sql + ' FROM inserted FOR XML RAW';

    DECLARE @x as XML;
    SET @x = CAST(EXEC(@sql) AS XML);


    .. use @x

END
15
Akash Kava

Innerhalb des Triggers können Sie COLUMNS_UPDATED() wie folgt verwenden, um einen aktualisierten Wert zu erhalten

-- Get the table id of the trigger
--
DECLARE @idTable      INT

SELECT  @idTable = T.id 
FROM    sysobjects P JOIN sysobjects T ON P.parent_obj = T.id 
WHERE   P.id = @@procid

-- Get COLUMNS_UPDATED if update
--
DECLARE @Columns_Updated VARCHAR(50)

SELECT  @Columns_Updated = ISNULL(@Columns_Updated + ', ', '') + name 
FROM    syscolumns 
WHERE   id = @idTable   
AND     CONVERT(VARBINARY,REVERSE(COLUMNS_UPDATED())) & POWER(CONVERT(BIGINT, 2), colorder - 1) > 0

Dieser Code-Ausschnitt schlägt jedoch fehl, wenn Sie eine Tabelle mit mehr als 62 Spalten haben. Arth.Overflow ...

Hier ist die endgültige Version, die mehr als 62 Spalten verarbeitet, aber nur die Anzahl der aktualisierten Spalten angibt. Es ist einfach, mit 'syscolumns' zu verknüpfen, um den Namen zu erhalten

DECLARE @Columns_Updated VARCHAR(100)
SET     @Columns_Updated = ''   

DECLARE @maxByteCU INT
DECLARE @curByteCU INT
SELECT  @maxByteCU = DATALENGTH(COLUMNS_UPDATED()), 
        @curByteCU = 1

WHILE @curByteCU <= @maxByteCU BEGIN
    DECLARE @cByte INT
    SET     @cByte = SUBSTRING(COLUMNS_UPDATED(), @curByteCU, 1)

    DECLARE @curBit INT
    DECLARE @maxBit INT
    SELECT  @curBit = 1, 
            @maxBit = 8
    WHILE @curBit <= @maxBit BEGIN
        IF CONVERT(BIT, @cByte & POWER(2,@curBit - 1)) <> 0 
            SET @Columns_Updated = @Columns_Updated + '[' + CONVERT(VARCHAR, 8 * (@curByteCU - 1) + @curBit) + ']'
        SET @curBit = @curBit + 1
    END
    SET @curByteCU = @curByteCU + 1
END
14
podosta

Ich habe eine andere, völlig andere Lösung, die COLUMNS_UPDATED überhaupt nicht verwendet und sich auch nicht auf die Erstellung von dynamischem SQL zur Laufzeit stützt. (Möglicherweise möchten Sie zur Entwurfszeit dynamisches SQL verwenden, aber das ist eine andere Geschichte.)

Grundsätzlich beginnen Sie mit den eingefügten und gelöschten Tabellen . Deaktivieren Sie jede dieser Tabellen, sodass Sie nur noch die Spalten für den eindeutigen Schlüssel, den Feldwert und den Feldnamen für jede Tabelle haben. Dann verbinden Sie die beiden und filtern nach Änderungen.

Hier ist ein voll funktionsfähiges Beispiel, einschließlich einiger Testaufrufe, um zu zeigen, was protokolliert wird.

-- -------------------- Setup tables and some initial data --------------------
CREATE TABLE dbo.Sample_Table (ContactID int, Forename varchar(100), Surname varchar(100), Extn varchar(16), Email varchar(100), Age int );
INSERT INTO Sample_Table VALUES (1,'Bob','Smith','2295','[email protected]',24);
INSERT INTO Sample_Table VALUES (2,'Alice','Brown','2255','[email protected]',32);
INSERT INTO Sample_Table VALUES (3,'Reg','Jones','2280','[email protected]',19);
INSERT INTO Sample_Table VALUES (4,'Mary','Doe','2216','[email protected]',28);
INSERT INTO Sample_Table VALUES (5,'Peter','Nash','2214','[email protected]',25);

CREATE TABLE dbo.Sample_Table_Changes (ContactID int, FieldName sysname, FieldValueWas sql_variant, FieldValueIs sql_variant, modified datetime default (GETDATE()));

GO

-- -------------------- Create trigger --------------------
CREATE TRIGGER TriggerName ON dbo.Sample_Table FOR DELETE, INSERT, UPDATE AS
BEGIN
    SET NOCOUNT ON;
    --Unpivot deleted
    WITH deleted_unpvt AS (
        SELECT ContactID, FieldName, FieldValue
        FROM 
           (SELECT ContactID
                , cast(Forename as sql_variant) Forename
                , cast(Surname as sql_variant) Surname
                , cast(Extn as sql_variant) Extn
                , cast(Email as sql_variant) Email
                , cast(Age as sql_variant) Age
           FROM deleted) p
        UNPIVOT
           (FieldValue FOR FieldName IN 
              (Forename, Surname, Extn, Email, Age)
        ) AS deleted_unpvt
    ),
    --Unpivot inserted
    inserted_unpvt AS (
        SELECT ContactID, FieldName, FieldValue
        FROM 
           (SELECT ContactID
                , cast(Forename as sql_variant) Forename
                , cast(Surname as sql_variant) Surname
                , cast(Extn as sql_variant) Extn
                , cast(Email as sql_variant) Email
                , cast(Age as sql_variant) Age
           FROM inserted) p
        UNPIVOT
           (FieldValue FOR FieldName IN 
              (Forename, Surname, Extn, Email, Age)
        ) AS inserted_unpvt
    )

    --Join them together and show what's changed
    INSERT INTO Sample_Table_Changes (ContactID, FieldName, FieldValueWas, FieldValueIs)
    SELECT Coalesce (D.ContactID, I.ContactID) ContactID
        , Coalesce (D.FieldName, I.FieldName) FieldName
        , D.FieldValue as FieldValueWas
        , I.FieldValue AS FieldValueIs 
    FROM 
        deleted_unpvt d

            FULL OUTER JOIN 
        inserted_unpvt i
            on      D.ContactID = I.ContactID 
                AND D.FieldName = I.FieldName
    WHERE
         D.FieldValue <> I.FieldValue --Changes
        OR (D.FieldValue IS NOT NULL AND I.FieldValue IS NULL) -- Deletions
        OR (D.FieldValue IS NULL AND I.FieldValue IS NOT NULL) -- Insertions
END
GO
-- -------------------- Try some changes --------------------
UPDATE Sample_Table SET age = age+1;
UPDATE Sample_Table SET Extn = '5'+Extn where Extn Like '221_';

DELETE FROM Sample_Table WHERE ContactID = 3;

INSERT INTO Sample_Table VALUES (6,'Stephen','Turner','2299','[email protected]',25);

UPDATE Sample_Table SET ContactID = 7 where ContactID = 4; --this will be shown as a delete and an insert
-- -------------------- See the results --------------------
SELECT *, SQL_VARIANT_PROPERTY(FieldValueWas, 'BaseType') FieldBaseType, SQL_VARIANT_PROPERTY(FieldValueWas, 'MaxLength') FieldMaxLength from Sample_Table_Changes;

-- -------------------- Cleanup --------------------
DROP TABLE dbo.Sample_Table; DROP TABLE dbo.Sample_Table_Changes;

Also kein Herumspielen mit Bigint-Bitfeldern und Arth-Überlaufproblemen. Wenn Sie die Spalten kennen, die Sie zur Entwurfszeit vergleichen möchten, benötigen Sie kein dynamisches SQL.

Auf der anderen Seite hat die Ausgabe ein anderes Format und alle Feldwerte werden in sql_variant konvertiert. Die erste kann durch erneutes Schwenken der Ausgabe und die zweite durch Zurücksetzen auf die erforderlichen Typen behoben werden, basierend auf Ihren Kenntnissen der Design der Tabelle, aber beide würden einige komplexe dynamische SQL erfordern. Beides ist möglicherweise kein Problem in Ihrer XML-Ausgabe. Diese Frage bewirkt etwas Ähnliches, um die Ausgabe wieder im gleichen Format zu erhalten.

Bearbeiten: Wenn Sie die folgenden Kommentare lesen und einen natürlichen Primärschlüssel haben, der sich ändern könnte, können Sie diese Methode weiterhin verwenden. Sie müssen nur eine Spalte hinzufügen, die standardmäßig mit einem GUID gefüllt ist, indem Sie die NEWID () - Funktion verwenden. Sie verwenden dann diese Spalte anstelle des Primärschlüssels.

Möglicherweise möchten Sie diesem Feld einen Index hinzufügen. Da sich die gelöschten und eingefügten Tabellen in einem Trigger jedoch im Speicher befinden, werden sie möglicherweise nicht verwendet und wirken sich möglicherweise negativ auf die Leistung aus.

19
Stephen Turner

Ich habe es als einfaches "Einzeiler" gemacht. Ohne Verwendung von Pivot, Schleifen, vielen Variablen usw. sieht es aus wie prozedurale Programmierung. SQL sollte verwendet werden, um Datensätze zu verarbeiten :-), die Lösung ist:

DECLARE @sql as NVARCHAR(1024);

select @sql = coalesce(@sql + ',' + quotename(column_name), quotename(column_name))
from INFORMATION_SCHEMA.COLUMNS
where substring(columns_updated(), columnproperty(object_id(table_schema + '.' + table_name, 'U'), column_name, 'columnId') / 8 + 1, 1) & power(2, -1 + columnproperty(object_id(table_schema + '.' + table_name, 'U'), column_name, 'columnId') % 8 ) > 0
    and table_name = 'DBCustomers'
    -- and column_name in ('c1', 'c2') -- limit to specific columns
    -- and column_name not in ('c3', 'c4') -- or exclude specific columns

SET @sql = 'SELECT ' + @sql + ' FROM inserted FOR XML RAW';

DECLARE @x as XML;
SET @x = CAST(EXEC(@sql) AS XML);

Es verwendet COLUMNS_UPDATED , kümmert sich um mehr als acht Spalten - es verarbeitet so viele Spalten, wie Sie möchten.

Es kümmert sich um die richtige Spaltenreihenfolge, die mit COLUMNPROPERTY erhalten werden sollte.

Es basiert auf der Ansicht SPALTEN , sodass nur bestimmte Spalten eingeschlossen oder ausgeschlossen werden können.

4
Michał Powaga

Der folgende Code funktioniert für mehr als 64 Spalten und protokolliert nur die aktualisierten Spalten. Befolgen Sie die Anweisungen in den Kommentaren und alles sollte gut sein.

/*******************************************************************************************
 *         Add the below table to your database to track data changes using the trigger    *
 *         below. Remember to change the variables in the trigger to match the table that  *
 *         will be firing the trigger                                                      *
 *******************************************************************************************/
SET ANSI_NULLS ON;
GO

SET QUOTED_IDENTIFIER ON;
GO

CREATE TABLE [dbo].[AuditDataChanges]
(
  [RecordId] [INT] IDENTITY(1, 1)
                   NOT NULL ,
  [TableName] [VARCHAR](50) NOT NULL ,
  [RecordPK] [VARCHAR](50) NOT NULL ,
  [ColumnName] [VARCHAR](50) NOT NULL ,
  [OldValue] [VARCHAR](50) NULL ,
  [NewValue] [VARCHAR](50) NULL ,
  [ChangeDate] [DATETIME2](7) NOT NULL ,
  [UpdatedBy] [VARCHAR](50) NOT NULL ,
  CONSTRAINT [PK_AuditDataChanges] PRIMARY KEY CLUSTERED
    ( [RecordId] 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 TABLE [dbo].[AuditDataChanges] ADD  CONSTRAINT [DF_AuditDataChanges_ChangeDate]  DEFAULT (GETDATE()) FOR [ChangeDate];
GO



/************************************************************************************************
 * Add the below trigger to any table you want to audit data changes on. Changes will be saved  *
 * in the AuditChangesTable.                                                                    *
 ************************************************************************************************/


ALTER TRIGGER trg_Survey_Identify_Updated_Columns ON Survey --Change to match your table name
   FOR INSERT, UPDATE
AS
SET NOCOUNT ON;

DECLARE @sql VARCHAR(5000) ,
    @sqlInserted NVARCHAR(500) ,
    @sqlDeleted NVARCHAR(500) ,
    @NewValue NVARCHAR(100) ,
    @OldValue NVARCHAR(100) ,
    @UpdatedBy VARCHAR(50) ,
    @ParmDefinitionD NVARCHAR(500) ,
    @ParmDefinitionI NVARCHAR(500) ,
    @TABLE_NAME VARCHAR(100) ,
    @COLUMN_NAME VARCHAR(100) ,
    @modifiedColumnsList NVARCHAR(4000) ,
    @ColumnListItem NVARCHAR(500) ,
    @Pos INT ,
    @RecordPk VARCHAR(50) ,
    @RecordPkName VARCHAR(50);

SELECT  *
INTO    #deleted
FROM    deleted;
SELECT  *
INTO    #Inserted
FROM    inserted;

SET @TABLE_NAME = 'Survey'; ---Change to your table name
SELECT  @UpdatedBy = UpdatedBy --Change to your column name for the user update field
FROM    inserted;
SELECT  @RecordPk = SurveyId --Change to the table primary key field
FROM    inserted;   
SET @RecordPkName = 'SurveyId';
SET @modifiedColumnsList = STUFF(( SELECT   ',' + name
                                   FROM     sys.columns
                                   WHERE    object_id = OBJECT_ID(@TABLE_NAME)
                                            AND SUBSTRING(COLUMNS_UPDATED(),
                                                          ( ( column_id
                                                          - 1 ) / 8 + 1 ),
                                                          1) & ( POWER(2,
                                                          ( ( column_id
                                                          - 1 ) % 8 + 1 )
                                                          - 1) ) = POWER(2,
                                                          ( column_id - 1 )
                                                          % 8)
                                 FOR
                                   XML PATH('')
                                 ), 1, 1, '');


WHILE LEN(@modifiedColumnsList) > 0
    BEGIN
        SET @Pos = CHARINDEX(',', @modifiedColumnsList);
        IF @Pos = 0
            BEGIN
                SET @ColumnListItem = @modifiedColumnsList;
            END;
        ELSE
            BEGIN
                SET @ColumnListItem = SUBSTRING(@modifiedColumnsList, 1,
                                                @Pos - 1);
            END;    

        SET @COLUMN_NAME = @ColumnListItem;
        SET @ParmDefinitionD = N'@OldValueOut NVARCHAR(100) OUTPUT';
        SET @ParmDefinitionI = N'@NewValueOut NVARCHAR(100) OUTPUT';
        SET @sqlDeleted = N'SELECT @OldValueOut=' + @COLUMN_NAME
            + ' FROM #deleted where ' + @RecordPkName + '='
            + CONVERT(VARCHAR(50), @RecordPk);
        SET @sqlInserted = N'SELECT @NewValueOut=' + @COLUMN_NAME
            + ' FROM #Inserted where ' + @RecordPkName + '='
            + CONVERT(VARCHAR(50), @RecordPk);
        EXECUTE sp_executesql @sqlDeleted, @ParmDefinitionD,
            @OldValueOut = @OldValue OUTPUT;
        EXECUTE sp_executesql @sqlInserted, @ParmDefinitionI,
            @NewValueOut = @NewValue OUTPUT;
        IF ( LTRIM(RTRIM(@NewValue)) != LTRIM(RTRIM(@OldValue)) )
            BEGIN   
                SET @sql = 'INSERT INTO [dbo].[AuditDataChanges]
                                               ([TableName]
                                               ,[RecordPK]
                                               ,[ColumnName]
                                               ,[OldValue]
                                               ,[NewValue]
                                               ,[UpdatedBy])
                                         VALUES
                                               (' + QUOTENAME(@TABLE_NAME, '''') + '
                                               ,' + QUOTENAME(@RecordPk, '''') + '
                                               ,' + QUOTENAME(@COLUMN_NAME, '''') + '
                                               ,' + QUOTENAME(@OldValue, '''') + '
                                               ,' + QUOTENAME(@NewValue, '''') + '
                                               ,' + QUOTENAME(@UpdatedBy, '''') + ')';


                EXEC (@sql);
            END;     
        SET @COLUMN_NAME = '';
        SET @NewValue = '';
        SET @OldValue = '';
        IF @Pos = 0
            BEGIN
                SET @modifiedColumnsList = '';
            END;
        ELSE
            BEGIN
           -- start substring at the character after the first comma
                SET @modifiedColumnsList = SUBSTRING(@modifiedColumnsList,
                                                     @Pos + 1,
                                                     LEN(@modifiedColumnsList)
                                                     - @Pos);
            END;
    END;
DROP TABLE #Inserted;
DROP TABLE #deleted;

GO
1
Rick

Ich habe die akzeptierte Antwort transformiert, um eine durch Komma getrennte Liste der Spaltennamen zu erhalten (gemäß der Empfehlung des Autors). Ausgabe - "Columns_Updated" als 'Column1, Column2, Column5'

-- get names of updated columns
DECLARE @idTable      INT
declare @ColumnName nvarchar(300)
declare @ColId int

SELECT  @idTable = T.id 
FROM    sysobjects P JOIN sysobjects T ON P.parent_obj = T.id 
WHERE   P.id = @@procid

DECLARE @changedProperties nvarchar(max) = ''

DECLARE @Columns_Updated VARCHAR(2000) = ''

DECLARE @maxByteCU INT
DECLARE @curByteCU INT
SELECT  @maxByteCU = DATALENGTH(COLUMNS_UPDATED()), 
        @curByteCU = 1

WHILE @curByteCU <= @maxByteCU BEGIN
    DECLARE @cByte INT
    SET     @cByte = SUBSTRING(COLUMNS_UPDATED(), @curByteCU, 1)

    DECLARE @curBit INT
    DECLARE @maxBit INT
    SELECT  @curBit = 1, 
            @maxBit = 8
    WHILE @curBit <= @maxBit BEGIN
        IF CONVERT(BIT, @cByte & POWER(2, @curBit - 1)) <> 0 BEGIN
            SET @ColId = cast( CONVERT(VARCHAR, 8 * (@curByteCU - 1) + @curBit) as int)

            select @ColumnName = [Name]
            FROM syscolumns 
            WHERE id = @idTable and colid = @ColId

            SET @Columns_Updated = @Columns_Updated + ',' + @ColumnName
        END
        SET @curBit = @curBit + 1
    END
    SET @curByteCU = @curByteCU + 1
END
1

Die einzige Möglichkeit, die mir einfällt, um dies zu erreichen, ohne Spaltennamen hart zu codieren, besteht darin, den Inhalt der gelöschten Tabelle in eine temporäre Tabelle zu verschieben und dann eine Abfrage basierend auf der Tabellendefinition zu erstellen, um den Inhalt Ihrer temporären Tabelle zu vergleichen und die tatsächliche Tabelle und geben eine Liste mit getrennten Spalten zurück, basierend darauf, ob sie übereinstimmen oder nicht. Zugegeben, das Folgende ist ausführlich.

Declare @sql nvarchar(4000)
DECLARE @ParmDefinition nvarchar(500)
Declare @OutString varchar(8000)
Declare @tbl sysname

Set @OutString = ''
Set @tbl = 'SomeTable' --The table we are interested in
--Store the contents of deleted in temp table
Select * into #tempDelete from deleted 
--Build sql string based on definition 
--of table 
--to retrieve the column name
--or empty string
--based on comparison between
--target table and temp table
set @sql = ''
Select @sql = @sql + 'Case when IsNull(i.[' + Column_Name + 
'],0) = IsNull(d.[' + Column_name + '],0) then '''' 
 else ' + quotename(Column_Name, char(39)) + ' + '',''' + ' end +'
from information_schema.columns 
where table_name = @tbl
--Define output parameter
set @ParmDefinition = '@OutString varchar(8000) OUTPUT'
--Format sql
set @sql = 'Select @OutString = ' 
+ Substring(@sql,1 , len(@sql) -1) + 
' From SomeTable i  ' --Will need to be updated for target schema
+ ' inner join #tempDelete d on
i.PK = d.PK ' --Will need to be updated for target schema
--Execute sql and retrieve desired column list in output parameter
exec sp_executesql @sql, @ParmDefinition, @OutString OUT
drop table  #tempDelete
--strip trailing column if a non-zero length string 
--was returned
if Len(@Outstring) > 0 
    Set @OutString = Substring(@OutString, 1, Len(@Outstring) -1)
--return comma delimited list of changed columns. 
Select @OutString 
End
1
cmsjr

Dem von Rick bereitgestellten Beispielcode fehlt die Behandlung für die Aktualisierung mehrerer Zeilen.

Bitte lassen Sie mich Ricks Version wie folgt verbessern:

USE [AFC]
GO

/****** Object:  Trigger [dbo].[trg_Survey_Identify_Updated_Columns]    Script Date: 27/7/2018 14:08:49 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

ALTER TRIGGER [dbo].[trg_Survey_Identify_Updated_Columns] ON [dbo].[Sample_Table] --Change to match your table name
FOR INSERT
        ,UPDATE
AS
SET NOCOUNT ON;

DECLARE @sql VARCHAR(5000)
        ,@sqlInserted NVARCHAR(500)
        ,@sqlDeleted NVARCHAR(500)
        ,@NewValue NVARCHAR(100)
        ,@OldValue NVARCHAR(100)
        ,@UpdatedBy VARCHAR(50)
        ,@ParmDefinitionD NVARCHAR(500)
        ,@ParmDefinitionI NVARCHAR(500)
        ,@TABLE_NAME VARCHAR(100)
        ,@COLUMN_NAME VARCHAR(100)
        ,@modifiedColumnsList NVARCHAR(4000)
        ,@ColumnListItem NVARCHAR(500)
        ,@Pos INT
        ,@RecordPk VARCHAR(50)
        ,@RecordPkName VARCHAR(50);

SELECT *
INTO #deleted
FROM deleted;

SELECT *
INTO #Inserted
FROM inserted;

SET @TABLE_NAME = 'Sample_Table';---Change to your table name

DECLARE t_cursor CURSOR
FOR
SELECT ContactID 
FROM inserted

OPEN t_cursor

FETCH NEXT
FROM t_cursor
INTO @RecordPk 

WHILE @@FETCH_STATUS = 0
BEGIN
        --SELECT @UpdatedBy = Surname --Change to your column name for the user update field
        --FROM inserted;
        --SELECT @RecordPk = ContactID --Change to the table primary key field
        --FROM inserted;
        SET @RecordPkName = 'ContactID';
        SET @modifiedColumnsList = STUFF((
                                SELECT ',' + name
                                FROM sys.columns
                                WHERE object_id = OBJECT_ID(@TABLE_NAME)
                                        AND SUBSTRING(COLUMNS_UPDATED(), ((column_id - 1) / 8 + 1), 1) & (POWER(2, ((column_id - 1) % 8 + 1) - 1)) = POWER(2, (column_id - 1) % 8)
                                FOR XML PATH('')
                                ), 1, 1, '');

        WHILE LEN(@modifiedColumnsList) > 0
        BEGIN
                SET @Pos = CHARINDEX(',', @modifiedColumnsList);

                IF @Pos = 0
                BEGIN
                        SET @ColumnListItem = @modifiedColumnsList;
                END;
                ELSE
                BEGIN
                        SET @ColumnListItem = SUBSTRING(@modifiedColumnsList, 1, @Pos - 1);
                END;

                SET @COLUMN_NAME = @ColumnListItem;
                SET @ParmDefinitionD = N'@OldValueOut NVARCHAR(100) OUTPUT';
                SET @ParmDefinitionI = N'@NewValueOut NVARCHAR(100) OUTPUT';
                SET @sqlDeleted = N'SELECT @OldValueOut=' + @COLUMN_NAME + ' FROM #deleted where ' + @RecordPkName + '=' + CONVERT(VARCHAR(50), @RecordPk);
                SET @sqlInserted = N'SELECT @NewValueOut=' + @COLUMN_NAME + ' FROM #Inserted where ' + @RecordPkName + '=' + CONVERT(VARCHAR(50), @RecordPk);

                EXECUTE sp_executesql @sqlDeleted
                        ,@ParmDefinitionD
                        ,@OldValueOut = @OldValue OUTPUT;

                EXECUTE sp_executesql @sqlInserted
                        ,@ParmDefinitionI
                        ,@NewValueOut = @NewValue OUTPUT;

                --PRINT @newvalue
                --PRINT @oldvalue

                IF (LTRIM(RTRIM(@NewValue)) != LTRIM(RTRIM(@OldValue)))
                BEGIN
                        SET @sql = 'INSERT INTO [dbo].[AuditDataChanges]
                                               ([TableName]
                                               ,[RecordPK]
                                               ,[ColumnName]
                                               ,[OldValue]
                                               ,[NewValue] )
                                         VALUES
                                               (' + QUOTENAME(@TABLE_NAME, '''') + '
                                               ,' + QUOTENAME(@RecordPk, '''') + '
                                               ,' + QUOTENAME(@COLUMN_NAME, '''') + '
                                               ,' + QUOTENAME(@OldValue, '''') + '
                                               ,' + QUOTENAME(@NewValue, '''') + '
                                               '  + ')';

                        EXEC (@sql);
                END;

                SET @COLUMN_NAME = '';
                SET @NewValue = '';
                SET @OldValue = '';

                IF @Pos = 0
                BEGIN
                        SET @modifiedColumnsList = '';
                END;
                ELSE
                BEGIN
                        -- start substring at the character after the first comma
                        SET @modifiedColumnsList = SUBSTRING(@modifiedColumnsList, @Pos + 1, LEN(@modifiedColumnsList) - @Pos);
                END;
        END;

        FETCH NEXT
        FROM t_cursor
        INTO @RecordPk 
END

DROP TABLE #Inserted;

DROP TABLE #deleted;

CLOSE t_cursor;

DEALLOCATE t_cursor;
0
RAY