Показать сообщение отдельно
Старый 26.01.2007, 09:51   #14  
Sequel is offline
Sequel
Участник
 
53 / 13 (1) ++
Регистрация: 17.06.2005
Адрес: Новокузнецк
SQL Server 2005 Enterprice Edition
База данных работает в версионном режиме.
Имеется простенькая табличка:

CREATE TABLE [dbo].[_Table1](
[id] [varchar](10) COLLATE Cyrillic_General_CI_AS NOT NULL,
[name] [varchar](50) COLLATE Cyrillic_General_CI_AS NOT NULL,
[flag] [int] NOT NULL,
[dataareaid] [varchar](10) COLLATE Cyrillic_General_CI_AS NOT NULL

Первичный ключ PK__Table1 в ней: по полям dataareaid и id , ключ является кластерным. Есть еще индекс IX__Table1 по полям dataareaid и flag. Использование поля dataareaid в качестве первого поля во всех индексах и ключах здесь принципиально: в Аксапте это поле является идентификатором компании, используется практически во всех таблицах, и включается во все ключи и индексы как первое поле.
Мы хотим выполнить запрос на обновление записи (записей) в этой таблицы:

SELECT * FROM _TABLE1 WITH( INDEX(PK__Table1), UPDLOCK) WHERE dataareaid='comp' and (ID='2')

Причем выполнить в виде курсора используя хранимые процедуры sp_cursorprepexec, sp_cursorfetch и т.д. В результате список операций получается такой:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED

begin transaction

DECLARE @p1 int
SET @p1=-1
DECLARE @p2 int
SET @p2=0
DECLARE @p5 int
SET @p5=2
DECLARE @p6 int
SET @p6=2
DECLARE @p7 int
SET @p7=-1

exec sp_cursorprepexec @p1 output,@p2 output,
NULL,
N'SELECT * FROM _TABLE1 WITH( INDEX(PK__Table1), UPDLOCK) WHERE dataareaid=''comp'' and (ID=''2'')',
@p5 output,@p6 output,@p7 output
select @p1, @p2, @p5, @p6, @p7

exec sp_cursorfetch 180150023,2,1,1

exec sp_cursor 180150023,40,1

exec sp_cursorclose 180150023
commit

Рассмотрим по порядку то, что здесь выполняется.
Оператор exec sp_cursorprepexec парсит и выполняет запрос, здесь важно обратить внимание на 2 параметра: p5 и p6, которые представляют из себя тип курсора (значение 2 соответствует динамическому типу) и тип конкурентности (2 – scroll locks) ,т.е. записи блокируются в тот момент, когда они выбираются командой fetch и разблокируются при переходе к следующей записи.
Выполняю эти действия пошагово, и смотрю блокировки в базе данных
После exec sp_cursorprepexec никаких блокировок нет.
После sp_cursorfetch блокируется ключ индекса PK__Table1, что в общем то никаких вопросов не вызывает.
А вот после выполнения exec sp_cursor возникает блокировка индекса IX__Table1 которая вызывает массу вопросов: я выбираю на изменение запись по значению первичного ключа, это значение первичного ключа должно заблокироваться, оно блокируется как и положено…но причем тут индекс по совершенно другому полю, почему он блокируется?
Причем блокировка индекса PK__Table1 проходит безобидно: я могу из другой транзакции выбрать точно так же на обновление любую другую запись этой же таблицы. А вот блокировка IX__Table1 приводит к тому, что попытка из другой транзакции выбрать на обновление любую запись из этой же таблицы приведет к блокировке ожидания, и запрос не выполнится до тех пор, пока первая транзакция не разблокирует индекс. Такое поведение SQL Server, естественно безобидным назвать нельзя, поэтому хотелось бы разобраться из-за чего такое возникает