Background and use-case I need to create a stored procedure where the input parameters will be: @sourceTable, @targetTable, @compositeKey. I'm using available fields for the composite key, since I need to create myself what makes the row unique. Also, I want to use this procedure on other tables in other scenarios with the same purpose. I think I need to use dynamic-SQL since this is the only way that supports having table names as parameters (might be wrong in this).
My main issues are that I've read that you should be very careful with dynamic-sql. Also my error-handling is probably not best-practice. So that I would very much need some feedback on... Read somewhere that I could use SET XACT_ABORT ON
and that would sort of take care of error-handling?
Goal
- Do INSERT based on composite-key from available fields. It should take the rows from the source table and if they do not exist in the target table, insert them. Otherwise do nothing.
- Print a row to a log table, specifying if the run was successful, the time of execute and number of rows affected.
Any help you guys give is helpful!
Outline of what I want to achieve
CREATE PROCEDURE [dbo].[usp_InsertByCompositeKey]
@targetTable NVARCHAR(255),
@sourceTable NVARCHAR(255),
@compositeKey NVARCHAR(MAX)
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION;
--do dynamic-sql here.
COMMIT;
END TRY
BEGIN CATCH
ROLLBACK;
END CATCH
END
Current code within the BEGIN TRANSACTION-block:
DECLARE @SqlQuery NVARCHAR(MAX);
DECLARE @WhereClause NVARCHAR(MAX) = '';
-- Split the compositeKey string into individual rows/values with a temporary table with the column "Field".
DECLARE @CompositeKeyFields TABLE (Field NVARCHAR(255));
INSERT INTO @CompositeKeyFields
SELECT value FROM STRING_SPLIT(@compositeKey, '|');
-- Construct the WHERE clause dynamically using the rows in column "Field" in @CompositeKeyFields. Iterate through the rows and build the WHERE clause.
SELECT @WhereClause = @WhereClause + QUOTENAME(Field) + ' = S.' + QUOTENAME(Field) + ' AND '
FROM @CompositeKeyFields;
SET @WhereClause = LEFT(@WhereClause, LEN(@WhereClause) - LEN(' AND ')); -- We need to remove the trailing 'AND' from the iteration above.
-- Construct the dynamic SQL query for INSERT.
SET @SqlQuery =
'INSERT INTO ' + @targetTable + ' ' +
'SELECT * ' +
'FROM ' + @sourceTable + ' AS S' +
' WHERE NOT EXISTS (' +
' SELECT 1 FROM ' + @targetTable + ' AS T' +
' WHERE ' + @WhereClause +
' );';
-- Executes the dynamic SQL query
EXEC sp_executesql @SqlQuery;
-- Log how many rows that was inserted. Used for logging to AuditLog.
SET @InsertedRowCount = @@ROWCOUNT;
COMMIT;
-- Log the result of the insert to an AuditLog table
INSERT INTO [transform].[AuditLog] (LogMessage, LogDateTime, IsError, InsertedRowCount)
VALUES ('sp_InsertByCompositeKey executed successfully for source table ' + @sourceTable + ' and target table ' + @targetTable + ' with the composite key: ' + @compositeKey, GETDATE(), 0, @InsertedRowCount);