EDIT : 동시 환경 race conditions 방지 상관 하위 쿼리 WITH (UPDLOCK)
사용하거나 EXCEPT
뭔는 SELECT
d는. 아래에 쓴 테스트 스크립트는 현재 연결에서만 볼 수있는 임시 테이블을 사용하기 때문에 필요하지 않지만 실제 환경에서는 사용자 테이블에 대해 작동하는 것이 필요합니다.
MERGE
에는 UPDLOCK
이 필요하지 않습니다. MCL의 대답을 다시에서 영감을
: 고유 인덱스 & 나는 벤치 마크
conditional inserts 대
try/catch하기로 결정, 데이터베이스 오류가 발생 할 수 있습니다.
결과는 try/catch에서 조건부 삽입을 지원하지만 YMMV는 지원하는 것으로 보입니다. 그것은 등
여기 결과 (SQL 서버 2008 10.0.1600.2를 구축)이며, 하나의 시스템에서 실행되는 아주 간단한 시나리오 (등 하나 개의 컬럼, 작은 테이블,),이다 :
duplicates (short table)
try/catch: 14440 milliseconds/100000 inserts
conditional insert: 2983 milliseconds/100000 inserts
except: 2966 milliseconds/100000 inserts
merge: 2983 milliseconds/100000 inserts
uniques
try/catch: 3920 milliseconds/100000 inserts
conditional insert: 3860 milliseconds/100000 inserts
except: 3873 milliseconds/100000 inserts
merge: 3890 milliseconds/100000 inserts
straight insert: 3173 milliseconds/100000 inserts
duplicates (tall table)
try/catch: 14436 milliseconds/100000 inserts
conditional insert: 3063 milliseconds/100000 inserts
except: 3063 milliseconds/100000 inserts
merge: 3030 milliseconds/100000 inserts
공지 사항, 심지어 독특한 인서트에 대해서도 약간 이상의 조건부 삽입보다 더 많은 오버 헤드가 필요합니다. 버전, CPU, 코어 수 등에 따라 차이가 있는지 궁금합니다.
IF
조건부 삽입을 벤치 마크하지 않았습니다. 단지 WHERE
입니다. 나는 IF
버라이어티가 더 많은 오버 헤드를 보여줄 것이라고 가정한다. 왜냐하면 a) 두 개의 명령문을 갖고 있기 때문이다. 그리고 b) 트랜잭션에서 두 개의 명령문을 래핑하고 격리 수준을 직렬 가능 (!)으로 설정해야한다. 누군가 이을 테스트하려면 임시 테이블을 일반 사용자 테이블로 변경해야합니다 (직렬 테이블은 로컬 임시 테이블에 적용되지 않음). 당신이 트랩 중복에 필요하지 않은 경우, 당신은 항상 true로 설정 "중복 무시"와 고유 인덱스를 만들 수 있습니다
-- tested on SQL 2008.
-- to run on SQL 2005, comment out the statements using MERGE
set nocount on
if object_id('tempdb..#temp') is not null drop table #temp
create table #temp (col1 int primary key)
go
-------------------------------------------------------
-- duplicate insert test against a table w/ 1 record
-------------------------------------------------------
insert #temp values (1)
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
begin try
insert #temp select @x
end try
begin catch end catch
end
set @duration = datediff(ms,@now,getdate())
raiserror('duplicates (short table), 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 where col1 = @x)
end
set @duration = datediff(ms,@now,getdate())
raiserror('duplicates (short table), conditional insert: %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 except select col1 from #temp
end
set @duration = datediff(ms,@now,getdate())
raiserror('duplicates (short table), except: %i milliseconds/%i inserts',-1,-1,@duration, @y) with nowait
go
-- comment this batch out for SQL 2005
declare @x int, @y int, @now datetime, @duration int
select @x = 1, @y = 0, @now = getdate()
while @y < 100000 begin
set @y = @y+1
merge #temp t using (select @x) s (col1) on t.col1 = s.col1 when not matched by target then insert values (col1);
end
set @duration = datediff(ms,@now,getdate())
raiserror('duplicates (short table), merge: %i milliseconds/%i inserts',-1,-1,@duration, @y) with nowait
go
-------------------------------------------------------
-- unique insert test against an initially empty table
-------------------------------------------------------
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
end
set @duration = datediff(ms,@now,getdate())
raiserror('uniques, straight insert: %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
begin try
insert #temp select @x
end try
begin catch end catch
end
set @duration = datediff(ms,@now,getdate())
raiserror('uniques, 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 where col1 = @x)
end
set @duration = datediff(ms,@now,getdate())
raiserror('uniques, conditional insert: %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 except select col1 from #temp
end
set @duration = datediff(ms,@now,getdate())
raiserror('uniques, except: %i milliseconds/%i inserts',-1,-1,@duration, @x) with nowait
go
-- comment this batch out for SQL 2005
truncate table #temp
declare @x int, @now datetime, @duration int
select @x = 1, @now = getdate()
while @x < 100000 begin
set @x = @x+1
merge #temp t using (select @x) s (col1) on t.col1 = s.col1 when not matched by target then insert values (col1);
end
set @duration = datediff(ms,@now,getdate())
raiserror('uniques, merge: %i milliseconds/%i inserts',-1,-1,@duration, @x) with nowait
go
-------------------------------------------------------
-- duplicate insert test against a table w/ 100000 records
-------------------------------------------------------
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
end try
begin catch end catch
end
set @duration = datediff(ms,@now,getdate())
raiserror('duplicates (tall table), 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 where col1 = @x)
end
set @duration = datediff(ms,@now,getdate())
raiserror('duplicates (tall table), conditional insert: %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 except select col1 from #temp
end
set @duration = datediff(ms,@now,getdate())
raiserror('duplicates (tall table), except: %i milliseconds/%i inserts',-1,-1,@duration, @y) with nowait
go
-- comment this batch out for SQL 2005
declare @x int, @y int, @now datetime, @duration int
select @x = 1, @y = 0, @now = getdate()
while @y < 100000 begin
set @y = @y+1
merge #temp t using (select @x) s (col1) on t.col1 = s.col1 when not matched by target then insert values (col1);
end
set @duration = datediff(ms,@now,getdate())
raiserror('duplicates (tall table), merge: %i milliseconds/%i inserts',-1,-1,@duration, @y) with nowait
go
번째 접근 기억 거래 내에서 묶어야한다. 현명한 경우 동시성 문제가 발생할 수 있습니다. –
고유 한 색인을 만들 수 없습니까? 나는 ms SQL에 대한 경험이 없지만 그러한 indeces가 있어야한다고 생각한다. –
@valya : 사람들이 SQL Server가 가장 단순한 작업을 수행 할 수 있는지 의심스러워하는 방법. 고유 한 인덱스를 지원하지 않고 관계형 데이터베이스 엔진 *을 구현할 수 있는지 확실하지 않습니다. – Tomalak