MS SQL索引值更新

时间:2013-06-06 15:44:06

标签: sql-server-2008 tsql indexing sql-insert

设置: SQL Server 2008 R2

返回故事: 我们有一个proc,可以同时在多个线程中调用。这些线程proc调用有时会将重叠(重复)数据写入它们写入的表中。在insert语句中,我有一个“NOT EXISTS”子句,以确保没有插入重复项,但我们仍然得到重复项。

问题: “NOT EXISTS”子句执行简单的选择,是否可能,因为线程调用的插入仅相隔毫秒,某些索引(特别是“NOT EXISTS”子句使用的索引)尚未更新?因此在进行插入之前它没有看到现有记录吗?

思想: 这可能是我不理解SQL如何做它的事情。如果我有一个带有“WHERE NOT EXISTS”的插入,是否在插入之前检查以确保没有任何记录存在?或者在插入每一行时是否以逐行的方式检查?如果它是前者(在进行任何插入之前检查所有内容),那么我认为其他一个调用可能还没有完成它的插入。

我很难过。

这是我正在做的一个例子:

INSERT INTO [SomeTable] (Col1,Col2)
SELECT
    ColumnA,
    ColumnB
FROM
    #TempTable
WHERE 
    NOT EXISTS (
        SELECT 1
        FROM [SomeTable]
        WHERE Col1 = #TempTable.ColumnA
              AND Col2 = #TempTable.ColumnB
    )

2 个答案:

答案 0 :(得分:0)

您需要在交易期间锁定表格。其他线程将等待事务完成(提交或回滚)。

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ

BEGIN TRANSACTION

--do stuff

COMMIT TRANSACTION

http://msdn.microsoft.com/en-us/library/ms173763.aspx

答案 1 :(得分:0)

默认情况下,SQL Server使用READ COMMITTED隔离级别。这意味着在选择期间,每个行上的读锁定仅在select正在主动访问该行时保持。

这意味着在SELECT完成后INSERT发生之前,另一个线程可以插入该行,即使该间隙只有几纳秒长。

即使是MERGE语句也不会阻止并发插入,因为搜索部分和插入部分之间有一段时间。 (有关详细信息,请参阅http://sqlity.net/en/1645/merge-wonders-insert-or-use/。)

您需要在包含检查和以下插入的整个事务的非现有行上保留锁定。实现这一目标的唯一方法是使用包装事务并将事务隔离级别设置为SERIALIZABLE

类似的东西:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRAN
  IF(NOT EXISTS(SELECT ...))
  BEGIN
   INSERT....
  END;
COMMIT;

更新:

因为您提供了一个示例,现在尝试一起执行这两个步骤,让我从引用的文章中重新发布我的示例,删除所有删除的内容对于这种情况并不重要:

MERGE dbo.Product WITH(HOLDLOCK) AS p
USING (VALUES(@ProductName, @ProductNumber))n(Name,ProductNumber)
ON p.ProductNumber = n.ProductNumber
WHEN NOT MATCHED THEN
INSERT(Name, ProductNumber)
VALUES(n.Name, n.ProductNumber)

HOLDLOCK提示具有(本地)与将事务隔离级别设置为SERIALIZABLE相同的效果。因为MERGE是数据更改语句,所以它在事务中自动执行。

使用MERGE语句的另一个好处是,如果以后需要,您可以轻松添加UPDATE分支。