8

我在 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 个值来查找记录是否存在,因此唯一约束不起作用。编辑示例以反映...

4

6 回答 6

5

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

处理此问题的正确方法是对列强制执行唯一约束,并始终 INSERT 并处理约束冲突:

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

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

于 2010-02-17T19:03:19.813 回答
3

在几乎不并发的环境中,并发INSERT可能发生在您的第二个查询之间IF NOT EXISTS和之间。INSERT

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

但是,您不应仅依赖此行为。在 上放置一个附加UNIQUE约束value

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

于 2010-02-17T17:09:50.460 回答
1

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

这会比这两种方法中的任何一种都快。

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

于 2010-02-17T16:44:19.493 回答
1

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

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

由于它每分钟运行数百次,因此应将锁定提示添加到 SELECT 和事务中以避免竞争条件

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

但是,我提出的仅 INSERT 并忽略任何重复约束错误的想法也可以避免竞争条件。

于 2010-02-17T16:47:18.320 回答
1

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

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

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

唯一性检查是显式执行还是隐式执行,AFAIK 无关紧要。如果通过在 DBMS 的“内部”完成检查获得了什么,那么当存在重复项时,与引发和处理错误相关的开销可能会消耗掉该收益。


底线:假设索引已经到位,如果您仍然发现自己渴望性能,我的建议是您对三个建议的解决方案进行经验测试。编写一个模拟预期输入数据的小程序,然后将这三个解决方案中的每一个都删除数十亿行,包括合理数量的重复项。这样做,一定要发布你的结果:-)

于 2010-02-17T17:08:55.313 回答
0

如果我不得不猜测,我猜第二个选项会更快。如果存在失败,sql server 不必为插入进行任何类型的设置,而在第一个中,它可能会查找一些表和字段名称并为永远不会发生的插入做准备。但是,我会在查询分析器中尝试一下,看看计划是怎么说的。

于 2010-02-17T16:45:38.930 回答