0

这个问题与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 (...)

如何重写此语句以使其工作?

4

4 回答 4

3

使用合并

您的 SQL 失败,因为 2 个并发重叠和非常接近的调用都会在 INSERT 发生之前从 EXISTS 中获得“假”。所以他们都尝试插入,当然有一个失败了。

此处对此进行了更多解释:Upsert 的选择/插入版本:是否存在高并发设计模式?虽然这个答案很旧,但在添加 MERGE 之前适用

于 2013-06-11T08:50:23.020 回答
2

IF EXISTS ... UPDATE ...(and )的问题IF NOT EXISTS ... INSERT ...在于,在并发下,多个线程(事务)将执行该IF EXISTS部分并且都得出相同的结论(例如,它不存在)并尝试相应地采取行动。结果是所有线程都尝试插入导致密钥冲突。根据代码的不同,这可能会导致违反约束的错误、死锁、超时或更糟(丢失更新)。

您需要确保检查IF EXISTS和操作是原子的。在 SQL Server 2008 之前的解决方案涉及使用事务和锁定提示,并且非常容易出错(容易出错)。您可以使用发布 SQL Server 2008 MERGE,这将确保适当的原子性,就像单个语句一样,并且引擎了解您要执行的操作。

于 2013-06-11T08:53:22.677 回答
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');
于 2013-06-11T09:21:12.167 回答
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 (...)

这个博客中它解释了更多。

此外,这个有趣的博客是关于并发的。

于 2013-06-11T08:57:39.113 回答