重构SQL以避免使用TABLOCKX

时间:2011-10-07 14:32:44

标签: sql sql-server sql-server-2005

我有两张这样的表:

Table1                   Table2
----------------------------------
Table1Id IDENTITY        Table2Id
Table2Id NOT NULL        SomeStuff
                         SomeOtherStuff

对它们之间的Table2Id使用外键约束。不用说(但我还是说它)需要在其相关的Table1行之前插入Table2行。加载两个表的过程的性质在批量集合操作中这样做,这意味着我在@temp表中有一大堆Table1和Table2数据,这些数据是使用IDENTITY列创建的,用于跟踪事物。我目前正在进行这样的插入(为简洁省略了事务和错误处理):

DECLARE @currentTable2Id INT
SET @currentTable2Id = IDENT_CURRENT('dbo.Table2')
INSERT INTO dbo.Table2 WITH (TABLOCKX)
    ( SomeStuff, 
      SomeOtherStuff
    )
    SELECT WhateverStuff, 
           WhateverElse
    FROM @SomeTempTable
    ORDER BY SomeTempTableId

INSERT INTO dbo.Table1 
    ( Table2Id )
    SELECT @currentTable2Id + SomeTempTableId
    FROM @SomeTempTable
    ORDER BY SomeTempTableId

这很好用,插入后所有的关系都是合理的。但是,由于TABLOCKX,我们遇到了人们等待彼此的查询完成的常数情况,无论是这个“加载”查询,还是其他UPDATES和INSERTS(我正在使用{{1}选择)。项目的性质要求加载很多数据,因此有时这个过程可以运行20-30分钟。关于这种表现,我无能为力。相信我,我已经尝试过了。

我无法使用NOLOCK,因为DBA不允许用户在生产中发出此命令,我认为使用SET IDENTITY_INSERT ON无论如何都需要IDENTITY_INSERT。有没有办法在不使用TABLOCKX的情况下进行此类插入?

2 个答案:

答案 0 :(得分:2)

我假设您正在使用tablockx,以防止在进程持续时间内插入表2(从而增加标识值)。试试这个

DECLARE @t TABLE (Table2Id int), @currentTable2Id int

INSERT INTO dbo.Table2
    ( SomeStuff, 
      SomeOtherStuff
    )
OUTPUT INSERTED.Table2Id into @t
    SELECT WhateverStuff, 
           WhateverElse
    FROM @SomeTempTable
    ORDER BY SomeTempTableId

SELECT @currentTable2Id = Table2Id FROM @t

INSERT INTO dbo.Table1 
    ( Table2Id )
    SELECT @currentTable2Id + SomeTempTableId
    FROM @SomeTempTable
    ORDER BY SomeTempTableId

DELETE @t

答案 1 :(得分:2)

确保ID中有@SomeTempTable字段。在TempID中创建新列Table2。向ID添加行时,将@SomeTempTableTempID插入Table2。当您插入TempID以获取自动递增的Table1时,在联接中使用列Table2ID

这样的事情:

alter table Table2 add TempID int

go

declare @SomeTempTable table(ID int identity, WhateverStuff int, WhateverElse int)

insert into @SomeTempTable values(1, 1)
insert into @SomeTempTable values(2, 2)

insert into Table2(SomeStuff, SomeOtherStuff, TempID)
select WhateverStuff, WhateverElse, ID
from @SomeTempTable

insert into Table1(Table2Id)
select Table2ID
from @SomeTempTable as S
  inner join Table2 as T2
    on S.ID = T2.TempID

go

alter table Table2 drop column TempID    

您可以在每次运行之前清除TempID列,而不是添加和删除TempID列,因此以前运行的旧值不会混淆您的连接。