SQL Server Bulk Upsert using Table Valued Parameters
Not much background to this post. Had an interest in improving a part of some work code that did multiple upsert operations, and came up with a bulk insert stored procedure using the pattern described by Aaron Bertand. Uploading an example that uses the StackOverflow2010 database and Users table. Performance is greatly improved by using this over individual upsert statements.
Users table valued parameter:
USE [StackOverflow2010]GO/****** Object: Table Valued Parameter [dbo].[UsersTVP] ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TYPE [dbo].[UsersTVP] AS TABLE ([Id] [int] NULL,[AboutMe] [nvarchar](max) NULL,[Age] [int] NULL,[CreationDate] [datetime] NOT NULL DEFAULT GETDATE(),[DisplayName] [nvarchar](40) NOT NULL DEFAULT '',[DownVotes] [int] NOT NULL DEFAULT 0,[EmailHash] [nvarchar](40) NULL,[LastAccessDate] [datetime] NOT NULL DEFAULT GETDATE(),[Location] [nvarchar](100) NULL,[Reputation] [int] NOT NULL DEFAULT 1,[UpVotes] [int] NOT NULL DEFAULT 0,[Views] [int] NOT NULL DEFAULT 0,[WebsiteUrl] [nvarchar](200) NULL,[AccountId] [int] NULL)GO
UpsertUsers stored procedure:
USE [StackOverflow2010]GO/****** Object: StoredProcedure [dbo].[UpsertUsers] ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGO-- =============================================-- Author: Tom Barrett-- Create date: 2022-12-09-- Description: Upserts a list of Users using table valued parameter-- =============================================CREATE OR ALTER PROCEDURE [dbo].[UpsertUsers]-- Add the parameters for the stored procedure here@Users UsersTVP READONLYASBEGIN/* SET NOCOUNT ON added to prevent extra result sets from interfering with SELECT statements. */SET NOCOUNT ON;/* SET XACT_ABORT ON for proper rollback on errors */SET XACT_ABORT ON;IF NOT EXISTS (SELECT TOP 1 * FROM @Users)/*THROW 51000, '@Users can not be empty', 1;*/RETURNBEGIN TRYBEGIN TRANSACTION;UPDATE [dbo].[Users] WITH (UPDLOCK, SERIALIZABLE)SET [AboutMe] = [tvp].[AboutMe], [Age] = [tvp].[Age], [CreationDate] = [tvp].[CreationDate], [DisplayName] = [tvp].[DisplayName], [DownVotes] = [tvp].[DownVotes], [EmailHash] = [tvp].[EmailHash], [LastAccessDate] = [tvp].[LastAccessDate], [Location] = [tvp].[Location], [Reputation] = [tvp].[Reputation], [UpVotes] = [tvp].[UpVotes], [Views] = [tvp].[Views], [WebsiteUrl] = [tvp].[WebsiteUrl], [AccountId] = [tvp].[AccountId]FROM [dbo].[Users] AS [u]INNER JOIN @Users AS [tvp]ON [u].[Id] = [tvp].[Id];INSERT [dbo].[Users]( [AboutMe], [Age], [CreationDate], [DisplayName], [DownVotes], [EmailHash], [LastAccessDate], [Location], [Reputation], [UpVotes], [Views], [WebsiteUrl], [AccountId])SELECT [AboutMe], [Age], [CreationDate], [DisplayName], [DownVotes], [EmailHash], [LastAccessDate], [Location], [Reputation], [UpVotes], [Views], [WebsiteUrl], [AccountId]FROM @Users as [tvp]WHERE NOT EXISTS (SELECT 1 FROM [dbo].[Users] WHERE [Id] = [tvp].[Id]);COMMIT TRANSACTION;END TRYBEGIN CATCHROLLBACK;END CATCHIF @@TRANCOUNT > 0 ROLLBACK;ENDGO
Comments
Post a Comment