2013-03-13 191 views
5

我运行值选择我的临时表,并将其插入到数据库中,像这样一个存储过程:INSERT INTO ..选择..违反唯一约束

INSERT INTO emails (EmailAddress) (
    SELECT 
     DISTINCT eit.EmailAddress 
    FROM #EmailInfoTemp eit 
     LEFT JOIN emails ea 
     ON eit.EmailAddress = ea.EmailAddress 
    WHERE ea.EmailAddressID IS NULL ) 

在罕见的情况下(〜每对夫妇一次在处理数千个请求的服务器上花费数小时),然后我在EmailAddress列的索引上收到唯一约束错误“违反UNIQUE KEY约束”。

我可以确认我没有传入重复值。即使我是,它应该被DISTINCT抓住。

- SQL Server 2008的 -Stored PROC +不使用事务+ JDBC的CallableStatement

是可能发生的SELECT和随后的INSERT之间,不存在对相同/不同的存储过程已完成的INSERT另一个电话与类似的数据?如果是这样,那么防止这种情况的最好方法是什么?

一些想法:我们有许多重复的“客户端”实例在生产环境中同时与这个SQL Server进行通信,所以我的第一反应是并发问题,但我似乎无法自己复制它。这是我的最佳猜测,但到目前为止它已经走到了尽头。这种情况在我们的暂存环境中不会发生,与生产环境相比,负载无关紧要。这是我开始关注并发问题的主要原因。

+1

双方EmailAddress中的NULL值可能违反唯一键约束。如果此列可以为空,则可以将插入重写为“not exists”。 – 2013-03-13 15:00:05

+0

好点。我检查代码中的空值,但我想我可以在sql中再次检查它。 – 2013-03-13 15:06:39

回答

5

该错误可能是由两个会话同时执行插入操作引起的。

您可以使用MERGE使您的SQL代码更安全。正如Aaron Bertrand的评论所说(谢谢!),you have to include a with (holdlock) hint to make merge really safe

; merge emails e with (holdlock) 
using #EmailInfoTemp eit 
on  e.EmailAddress = eit.EmailAddress 
when not matched then insert 
     (EmailAddress) values (eit.EmailAddress) 

merge声明将采取适当的锁,以确保没有其他会话可以潜入之间它的“不匹配”检查和“插入”。

如果您不能使用merge,则可以解决客户端问题。确保没有两个插件同时运行。这通常易于使用mutex或其他同步构造。

+1

['MERGE'不一定安全,除非您添加'HOLDLOCK'提示](http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx )。 – 2013-03-13 14:53:44

+0

@AaronBertrand:谢谢。 MERGE的MSDN页面显示“指定READPAST时,如果[按目标]未匹配,那么INSERT可能会导致违反UNIQUE约束的INSERT操作。”所以我认为它是安全的,除非你指定'readpast'。 – Andomar 2013-03-13 14:56:50

+1

是的,微软通常不会记录所有可能中断的情况。该文档也没有提到[任何这些尚未解决的错误(滚动到底部)](http://www.sqlperformance.com/2013/02/t-sql-queries/another-合并-BUG)。 :-) – 2013-03-13 14:59:14

相关问题