在INSERT之前或之后哪个更快,EXISTS?

时间:2010-02-17 16:39:39

标签: sql sql-server tsql insert exists

我在SQL Server中有一个SP,每分钟运行数百次,需要检查数据库的传入流量。目前它执行以下操作

INSERT INTO table
SELECT @value1,@value2 WHERE NOT EXISTS 
(SELECT * FROM table WHERE value1 = @value1 AND value2 = @value2);

但是,我也可以选择

IF NOT EXISTS(SELECT * FROM table WHERE value1 = @value1 AND value2 = @value2)    
   INSERT INTO table (value1,value2) VALUES (@value1,@value2);

哪个会更快?我觉得它们之间并没有太大区别,但我在历史上对TSQL不是很擅长...... = /

更新:哎呀...意味着表示EXISTS使用多于1个值来查找是否存在记录,因此唯一约束将不起作用。编辑样本以反映......

6 个答案:

答案 0 :(得分:5)

两种变体都不正确。您将插入一对重复的@ value1,@ value2,保证

处理此问题的正确方法是对两个列强制执行唯一约束,并始终插入并处理约束违规:

ALTER TABLE Table ADD CONSTRAINT uniqueValue1Value UNIQUE (value1, values2);

并插入:

BEGIN TRY
   INSERT INTO Table (value1, value2) VALUES (@value1, @value2);
END TRY
BEGIN CATCH
   DECLARE @error_number int, @error_message NVARCHAR(4000), @xact_state INT;
   SET @error_number = ERROR_NUMBER();
   SET @error_message = ERROR_MESSAGE();
   SET @xact_state = XACT_STATE();
   IF (@xact_state = -1)
   BEGIN
     ROLLBACK TRANSACTION;
   END
   IF (@error_number != 2627) /* 2627 is ' Cannot insert duplicate key in object ...' */
   BEGIN
      RAISERROR(N'Error inserting into Table: %i %s', 16,1, @errror_number, @error_message);
   END
ENd CATCH

虽然这些看起来很复杂,但必须考虑一个名为正确性的细节。与基于锁定提示的解决方案相比,这简单得多。这也是最高效的解决方案:只有一个寻求。所有其他解决方案至少需要两次搜索(一次验证可以插入,一次插入)。

答案 1 :(得分:3)

在难以并发的环境中,第二个查询中INSERTIF NOT EXISTS之间可能会发生并发INSERT

您的第一个查询会将共享锁定放在它检查的记录上,直到查询结束时才会解除,因此在查询运行之前无法插入新记录。

但是,您不应该仅仅依赖此行为。在UNIQUE上添加value约束。

它不仅会使数据库更加一致,而且会创建一个索引,使第一个查询更快。

答案 2 :(得分:1)

如果您希望值是唯一的,为什么不在该值上创建唯一约束,在没有SELECT的情况下执行INSERT并优雅地处理约束违规错误?

这比任何一种方法都快。

此外,您的第一种方法不起作用 - 当您选择时,您已经插入了值,因此select会明显找到您刚刚插入的内容。

答案 3 :(得分:1)

只是这样做,并忽略任何错误(假设值的唯一约束)......

BEGIN TRY
    INSERT INTO Table (value) VALUES (@value);
END TRY
BEGIN CATCH
    PRINT 'it was already in there!'
END CATCH

由于每分钟运行数百次,因此应将锁定提示添加到SELECT并将事务添加到avoid a race condition

(SELECT * FROM Table WITH (UPDLOCK, HOLDLOCK)  WHERE value = @value);

然而,我提出的只是INSERT并忽略任何重复约束错误的想法也会避免竞争条件。

答案 4 :(得分:1)

在对这个问题及其答案添加了大量评论之后,我将继续自己回答这个问题。

我不希望在原始问题中提出的两个提议之间在性能上有任何重大差异。一方面,正如Ray所指出的,第二种方法可能会使您无法为插入做一些准备,但另一方面,RDBMS通常在批处理语句中表现最佳,如第一种解决方案。

KM和DVK建议添加UNIQUE约束,这将使隐含唯一性测试,但需要在INSERT语句周围添加某种错误处理。我很难发现为什么这应该添加任何额外的性能,假设您已经有一个覆盖两列的索引如果您没有此类索引,请添加该索引,并重新考虑您对提高性能的需求。

是否明确或隐含地执行唯一性检查应该与AFAIK无关。如果通过在DBMS的“内部”完成检查获得了任何好处,那么当重复存在时,该增益可能会被与引发和处理错误相关的开销所吞噬。


底线:假设索引已经到位,如果您仍然发现自己渴望性能,我建议您对三个建议的解决方案进行实证测试。制作一个模拟预期输入数据的小程序,然后用几十亿行吹掉三个解决方案中的每一个,包括可能的重复数量。这样做,一定要发布你的结果: - )

答案 5 :(得分:0)

如果我不得不猜测,我猜第二种选择会更快。如果存在失败,sql server将不必为插入执行任何类型的设置,而在第一个中,它可能会查找一些表和字段名称并准备从未发生过的插入。但是,我会在查询分析器中尝试它,看看计划的内容。