此问题与Occasionally Getting SqlException: Timeout expired有关。实际上,我在我的应用中大量使用IF EXISTS... UPDATE .. ELSE .. INSERT
。但是用户Remus Rusanu说你不应该使用它。为什么我不应该使用它以及它包含的危险。所以,如果我有
IF EXISTS (SELECT * FROM Table1 WHERE Column1='SomeValue')
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
ELSE
INSERT INTO Table1 VALUES (...)
如何重写此语句以使其有效?
答案 0 :(得分:3)
使用MERGE
您的SQL失败,因为在INSERT发生之前,2个并发重叠和非常接近的调用都会从EXISTS中获得“false”。所以他们都试图插入,当然一个失败。
这里有更多解释:Select / Insert version of an Upsert: is there a design pattern for high concurrency?这个答案虽旧,但在添加MERGE之前适用
答案 1 :(得分:2)
IF EXISTS ... UPDATE ...
(和IF NOT EXISTS ... INSERT ...
)的问题在于,在并发下,多个线程(事务)将执行IF EXISTS
部分,而都会得出相同的结论 (例如,它不存在)并尝试采取相应行动。结果是所有线程都尝试INSERT导致密钥违规。根据代码,这可能导致约束违规错误,死锁,超时或更糟(更新丢失)。
您需要确保检查IF EXISTS
和操作是原子的。在SQL Server 2008之前,解决方案涉及使用事务和锁定提示,并且非常容易出错(容易出错)。发布SQL Server 2008后,您可以使用MERGE
,这将确保正确的原子性,因为单个语句和引擎了解您要尝试的操作。
答案 2 :(得分:1)
合并,是在第一种情况下创建来比较2个表,所以如果是这种情况你可以使用合并。
请看下面的内容,这也是另一种选择。
在这种情况下,遗憾的是您可能会遇到并发问题。
--Just update a row
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
-- If 0 rows are updated ...
IF @@ROWCOUNT=0
--Insert Row
INSERT INTO Table1 VALUES (...)
在this博客中,它解释了更多。
另外this interesting blog是关于并发的。
答案 3 :(得分:1)
本例中的MERGE语句示例为:
MERGE INTO Table1 t1
USING (SELECT 'SomeValue' as Column_id FROM dual) t2 ON
(t1.column_id = t2.column_id)
WHEN MATCHED THEN
UPDATE SET(...)
WHEN NOT MATCHED THEN
INSERT (t1.column_id)
VALUES ('SomeValue');