In SQL Server, when using the OUTPUT clause with an UPDATE statement inside a TRY...CATCH block, capturing the ID of a specific row that causes an error is not straightforward, because if the error occurs during the statement execution, the entire update operation fails — and SQL Server does not commit any changes, nor does it populate the OUTPUT clause.
To track the ID of a row that causes an error during the update, especially when updating multiple rows, you can approach this in a two-step manner:
Approach: Update in a Cursor or Loop with TRY-CATCH Around Each Row
This method processes each row individually so that any error can be caught along with the ID.
Step-by-step solution:
CREATE PROCEDURE dbo.UpdateWithErrorTracking
AS
BEGIN
SET NOCOUNT ON;
DECLARE @ID INT;
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorNumber INT;
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
DECLARE @ErrorLine INT;
DECLARE @ErrorProcedure NVARCHAR(128);
-- Temp table to hold IDs of rows to update
DECLARE @IDsToUpdate TABLE (ID INT);
-- Fill with IDs to process
INSERT INTO @IDsToUpdate (ID)
SELECT ID FROM YourTable WHERE <YourConditions>;
DECLARE cur CURSOR LOCAL FAST_FORWARD FOR
SELECT ID FROM @IDsToUpdate;
OPEN cur;
FETCH NEXT FROM cur INTO @ID;
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
UPDATE YourTable
SET Col1 = 'NewValue'
OUTPUT inserted.ID, inserted.Col1
WHERE ID = @ID;
END TRY
BEGIN CATCH
-- Capture error info
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorNumber = ERROR_NUMBER(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE(),
@ErrorLine = ERROR_LINE(),
@ErrorProcedure = ERROR_PROCEDURE();
-- Log or raise the error with the ID that failed
RAISERROR('Error updating ID = %d: %s', 16, 1, @ID, @ErrorMessage);
-- Optionally insert into error log table
-- INSERT INTO ErrorLog (ID, ErrorMessage, ...)
-- VALUES (@ID, @ErrorMessage, ...);
END CATCH;
FETCH NEXT FROM cur INTO @ID;
END
CLOSE cur;
DEALLOCATE cur;
END;
Key Notes:
This method ensures fine-grained error handling per row.
You sacrifice some performance (because of row-by-row operation), but gain error traceability.
If performance is critical and the error frequency is low, you could try the bulk update first, and fall back to this method only in the CATCH.