Sql 插入之前或之后哪个更快?

Sql 插入之前或之后哪个更快?,sql,sql-server,tsql,insert,exists,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

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

如果您希望值是唯一的,为什么不在值上创建唯一约束,在不选择的情况下执行插入并优雅地处理约束冲突错误

这比这两种方法都快


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

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

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

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


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

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

我预计原质询中提出的两项建议在表现上不会有重大差异。一方面,正如Ray所指出的,第二种方法可以避免您为插入做一些准备,但另一方面,RDBMS通常在批处理语句中表现最好,就像第一种解决方案一样

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

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



一句话:假设一个指数已经到位,如果你仍然发现自己渴望表现,我的建议是你对建议的三个解决方案进行实证测试。编写一个模拟预期输入数据的小程序,并将三个解决方案中的每一个都用几十亿行(包括合理数量的副本)处理掉。执行此操作时,请确保发布您的结果:-)

在几乎不并发的环境中,如果在第二个查询中不存在
插入
之间可能会发生并发
插入

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

然而,你不应该仅仅依靠这种行为。对
附加
唯一
约束


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

这两种变体都不正确。您将插入成对重复的@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

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

我相信你的最后一段是错的。
WHERE
子句绑定到
SELECT
,它确实是在插入之前执行的。我想我犯了第一个StackOverflow错误,然后,我简化了这些示例!它实际上是在检查具有两个值的记录,这样记录就不是唯一的。。。我将对其进行编辑以反映这一点。我可以100%保证第一种方法有效,我已经测试了很多次。添加跨越多列的
唯一约束应该没有问题。真的吗?酷!我会调查的that@carlos:这确实是一个不同的问题,虽然有些关联。你的第二个选择并不安全。在
IF
INSERT
语句之间可以同时执行
INSERT
语句。另一方面,第一个选项是单个批处理语句,而第二个选项是多个过程式语句。RDBMS通常在批处理语句中非常有效,而在过程性/命令性代码中效率较低。也就是说,我不知道手边的两个语句中哪一个执行得最好:)谢谢KM,我现在正在select上使用(NOLOCK)。。。我也应该提到这一点@roryok,如果同时对此SQL进行两次调用(您说它每分钟运行数百次),并且具有相同的值,那么它们可能都不选择现有行,并且都尝试插入并创建重复的行,从而破坏您的
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