I have a small problem with an update trigger under SQL Server 2008 R2. I have installed a trigger to log the changes into a log table. This is working fine for me but not when I update multiple rows.
CREATE TRIGGER [dbo].[TR_CompaniesUpdated]
ON [dbo].[Companies]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE
@Return NVARCHAR(MAX), -- Is the JSON of the data
@ParameterSQL VARCHAR(MAX), -- Is the select we want to retreive in JSON format
-- Data from of the row updated
@ID INT,
@StatusID SMALLINT,
-- Old data from of the row
@StatusID2 SMALLINT
-- Declare the cursor
DECLARE InsertedCursor CURSOR FAST_FORWARD FOR
SELECT [ID], [StatusID] FROM INSERTED
-- Create the temporary table with logs so we'll need to do only one insert after this
CREATE TABLE #tempLog(
[ID] [int] NOT NULL,
[Data] [nvarchar](max) NOT NULL,
[ActionID] [int] NOT NULL,
[DtCreated] [datetime] NOT NULL,
[CreatedBy] [int] NOT NULL)
-- Save updated values into variables
OPEN InsertedCursor
FETCH NEXT FROM InsertedCursor
INTO @ID, @StatusID
WHILE @@fetch_status = 0
BEGIN
-- Save old values into variables
SELECT
@StatusID2 = [StatusID]
FROM DELETED
WHERE [ID] = @ID
SELECT * INTO #tempTable
FROM DELETED
WHERE [ID] = @ID
-- Build the select only to et the changed data
SET @ParameterSQL = 'SELECT '
IF (ISNULL(@StatusID, '') != ISNULL(@StatusID2, '')) SET @ParameterSQL = @ParameterSQL + '[StatusID],'
IF (@ParameterSQL != 'SELECT ')
BEGIN
-- Update the modified date
UPDATE [dbo].[AssessedLevelCostCentre]
SET [DtModified] = GETDATE()
WHERE [ID] = @ID
SET @ParameterSQL = SUBSTRING(@ParameterSQL, 0, LEN(@ParameterSQL)) + ' FROM #tempTable'
-- Execute the JSON function to get the result in JSON format
EXEC [dbo].[GetJSON] @ParameterSQL, @Return = @Return OUTPUT
-- Insert the Change Log with the old data
INSERT INTO #tempLog
SELECT
[ID] AS [ID],
@Return AS [Data],
[ActionID] AS [ActionID],
CASE
WHEN [DtModified] IS NULL
THEN [DtCreated]
ELSE [DtModified]
END AS [DtModified],
CASE
WHEN [ModifiedBy] IS NULL
THEN [CreatedBy]
ELSE [ModifiedBy]
END AS [CreatedBy]
FROM #tempTable
END
ELSE
BEGIN
UPDATE [dbo].[AssessedLevelCostCentre]
SET [ModifiedBy] = (SELECT [ModifiedBy] FROM #tempTable)
WHERE [ID] = @ID
END
DROP TABLE #tempTable
-- Advance the Cursor
FETCH NEXT FROM InsertedCursor
INTO @ID, @StatusID
END
CLOSE InsertedCursor
DEALLOCATE InsertedCursor
-- Insert the Change Log with the old data
INSERT INTO [dbo].[AssessedLevelsCostCentersLogs]
SELECT *
FROM #tempLog
DROP TABLE #tempLog
END
As you can see I have an stored procedure than returns me the JSON data, the problem is than the stored procedure get the data from a query and I can’t pass the DELETED or INSERTED tables, that’s the reason I used a temp table that seems to do the trick but adding the ID clause.
UPDATED
I updated the code, now I have a cursor but as I can see the performance of this is really slow. I’ll need this in this way cause I can’t change the application to create the JSON and I think I trigger like this will work fine. Any one have an idea or how to improve the performance of this?
Trigger in Sql server run once for each batch, so if you insert 5000 records in one statement, the trigger kicks off exactly once. What you have done is assume it will run for each individual record by setting the values you want to scalar variables which of course can only hold one value not 5000. You need to re-think your process to take advantage of joining to inserted and deleted not running through one record at a time.