我想做一些快速插入,但要避免重复到表中。出于争论的缘故,我们称其为MarketPrices,我一直在尝试两种方法来做,但不确定如何进行基准测试,这会更快。
INSERT INTO MarketPrices (SecurityCode, BuyPrice, SellPrice, IsMarketOpen) SELECT @SecurityCode, @BuyPrice, @SellPrice, @IsMarketOpen EXCEPT SELECT SecurityCode, BuyPrice, SellPrice, j.bool as IsActive FROM MarketPrices CROSS JOIN (SELECT 0 as bool UNION SELECT 1 as bool ) as j
或者
DECLARE @MktId int SET @MktId = (SELECT SecurityId FROM MarketPrices where SecurityCode = @SecurityCode and BuyPrice=@BuyPrice and SellPrice = @SellPrice) IF (@MktId is NULL) BEGIN INSERT INTO MarketPrices (SecurityCode, BuyPrice, SellPrice, IsMarketOpen) VALUES (@SecurityCode,@BuyPrice, @SellPrice, @IsMarketOpen) END
假定它@whatever是存储过程中的输入参数。
@whatever
我希望能够在BuyPrice或SellPrice或两者均不同于其他先前出现的情况下为每个SecurityCode插入新记录。我不在乎IsMarketOpen。
上述两种方法中有什么愚蠢的吗?一个比另一个快吗?
编辑 :为防止在并发环境中发生竞争,请WITH (UPDLOCK)在相关子查询或EXCEPT‘d中使用SELECT。我在下面编写的测试脚本不需要它,因为它使用仅对当前连接可见的临时表,但是在实际环境中,对用户表进行操作很有必要。
WITH (UPDLOCK)
EXCEPT
SELECT
MERGE不需要UPDLOCK。
MERGE
UPDLOCK
受到mcl的答案re:唯一索引的启发,并让数据库抛出错误,我决定对条件插入与try / catch进行基准测试。
结果似乎支持在try / catch上执行条件插入,但支持YMMV。这是一个非常简单的场景(一列,小表等),在一台机器上执行,等等。
结果如下(SQL Server 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)您需要将这两个语句包装在事务中并将隔离级别设置为可序列化(!)。如果有人 想 对此进行测试,则需要将临时表更改为常规用户表(可序列化不适用于本地临时表)。
IF
WHERE
这是脚本:
-- 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