Рефакторинг SQL, чтобы избежать использования TABLOCKX - PullRequest
4 голосов
/ 07 октября 2011

У меня есть две таблицы, подобные этой:

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

С ограничением внешнего ключа для Table2Id между ними. Само собой разумеется (все же я говорю это в любом случае), что строка Table2 должна быть вставлена ​​перед связанной строкой Table1. Природа процедуры, которая загружает обе таблицы, делает это в массовых операциях с множествами, то есть у меня есть целая куча данных Table1 и Table2 в таблице @temp, которая была создана со столбцом 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 мы сталкиваемся с постоянными ситуациями, когда люди ждут завершения запросов друг друга, будь то этот запрос «загрузки» или другие ОБНОВЛЕНИЯ и ВСТАВКИ (я использую NOLOCK для выбора ). Природа проекта требует, чтобы было загружено много данных, поэтому бывают случаи, когда эта процедура может выполняться в течение 20-30 минут. Я ничего не могу с этим поделать. Поверь мне, я пытался.

Я не могу использовать SET IDENTITY_INSERT ON, поскольку администраторы баз данных не позволяют пользователям вводить эту команду в производственном процессе, и я думаю, что для использования IDENTITY_INSERT в любом случае потребуется TABLOCKX. Есть ли способ, которым я могу сделать такую ​​вставку без использования TABLOCKX?

Ответы [ 2 ]

2 голосов
/ 07 октября 2011

Убедитесь, что у вас есть ID поле в @SomeTempTable.Создайте новый столбец TempID в Table2.Вставьте ID из @SomeTempTable в TempID при добавлении строк в 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 он может быть там, но его необходимо очищать перед каждым запуском, чтобы старые значения из предыдущих запусков не перепутали ваши объединения.

2 голосов
/ 07 октября 2011

Я предполагаю, что вы используете tablockx в попытке предотвратить вставку чего-либо еще в Table2 (и, таким образом, увеличить значение идентификатора) на время вашего процесса.Попробуйте вместо этого

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...