I want to do some quick inserts but avoid duplicates into a Table. For argument\'s sake lets call it MarketPrices, I\'ve been experimenting with two ways of doing it but not
Below I have added the top answers from Only inserting a row if it's not already there to Peter Radocchia's excellent answer.
The takeaway is that using the race safe with try/catch
technique is marginally (~1%) faster than race safe with updlock, holdlock
technique when there are no actual collisions (i.e. you expect that collisions will be very rare - this is the uniques
scenario), and is a little slower (~20%) when there are always collisions (this is the duplicates
scenario). This is not taking complex issues like lock escalation into account.
Here are the results (SQL Server 2014, build 12.0.2000.8):
duplicates (short table)
try/catch: 15546 milliseconds / 100000 inserts
conditional insert: 1460 milliseconds / 100000 inserts
except: 1490 milliseconds / 100000 inserts
merge: 1420 milliseconds / 100000 inserts
race safe with try/catch: 1650 milliseconds / 100000 inserts
race safe with updlock, holdlock: 1330 milliseconds / 100000 inserts
uniques
try/catch: 2266 milliseconds / 100000 inserts
conditional insert: 2156 milliseconds / 100000 inserts
except: 2273 milliseconds / 100000 inserts
merge: 2136 milliseconds / 100000 inserts
race safe with try/catch: 2400 milliseconds / 100000 inserts
race safe with updlock, holdlock: 2430 milliseconds / 100000 inserts
straight insert: 1686 milliseconds / 100000 inserts
duplicates (tall table)
try/catch: 15826 milliseconds / 100000 inserts
conditional insert: 1530 milliseconds / 100000 inserts
except: 1506 milliseconds / 100000 inserts
merge: 1443 milliseconds / 100000 inserts
race safe with try/catch: 1636 milliseconds / 100000 inserts
race safe with updlock, holdlock: 1426 milliseconds / 100000 inserts
Duplicates (short table) section:
declare @x int, @y int, @now datetime, @duration int
select @x = 1, @y = 0, @now = getdate()
while @y < 100000 begin
set @y = @y+1
begin try
insert #temp select @x where not exists (select * from #temp where col1 = @x)
end try
begin catch
if error_number() <> 2627
throw
end catch
end
set @duration = datediff(ms,@now,getdate())
raiserror('duplicates (short table), race safe with try/catch: %i milliseconds / %i inserts',-1,-1,@duration,@y) with nowait
go
declare @x int, @y int, @now datetime, @duration int
select @x = 1, @y = 0, @now = getdate()
while @y < 100000 begin
set @y = @y+1
insert #temp select @x where not exists (select * from #temp with (updlock, holdlock) where col1 = @x)
end
set @duration = datediff(ms,@now,getdate())
raiserror('duplicates (short table), race safe with updlock, holdlock: %i milliseconds / %i inserts',-1,-1,@duration, @y) with nowait
go
Uniques section
truncate table #temp
declare @x int, @now datetime, @duration int
select @x = 0, @now = getdate()
while @x < 100000 begin
set @x = @x+1
begin try
insert #temp select @x where not exists (select * from #temp where col1 = @x)
end try
begin catch
if error_number() <> 2627
throw
end catch
end
set @duration = datediff(ms,@now,getdate())
raiserror('uniques, race safe with try/catch: %i milliseconds / %i inserts',-1,-1,@duration, @x) with nowait
go
truncate table #temp
declare @x int, @now datetime, @duration int
select @x = 0, @now = getdate()
while @x < 100000 begin
set @x = @x+1
insert #temp select @x where not exists (select * from #temp with (updlock, holdlock) where col1 = @x)
end
set @duration = datediff(ms,@now,getdate())
raiserror('uniques, race safe with updlock, holdlock: %i milliseconds / %i inserts',-1,-1,@duration, @x) with nowait
go
Duplicates (tall table) section
declare @x int, @y int, @now datetime, @duration int
select @x = 1, @y = 0, @now = getdate()
while @y < 100000 begin
set @y = @y+1
begin try
insert #temp select @x where not exists (select * from #temp where col1 = @x)
end try
begin catch
if error_number() <> 2627
throw
end catch
end
set @duration = datediff(ms,@now,getdate())
raiserror('duplicates (tall table), race safe with try/catch: %i milliseconds / %i inserts',-1,-1,@duration,@y) with nowait
go
declare @x int, @y int, @now datetime, @duration int
select @x = 1, @y = 0, @now = getdate()
while @y < 100000 begin
set @y = @y+1
insert #temp select @x where not exists (select * from #temp with (updlock, holdlock) where col1 = @x)
end
set @duration = datediff(ms,@now,getdate())
raiserror('duplicates (tall table), race safe with updlock, holdlock: %i milliseconds / %i inserts',-1,-1,@duration, @y) with nowait
go