Заявление EXEC и объем сделки - PullRequest
1 голос
/ 31 мая 2011

Поначалу это казалось таким легким, но все, что я знаю, снова кажется неправильным.

Глядя на PAQ, кажется, что консенсус заключается в том, что EXEC не запускает неявную транзакцию, вы можете проверить это, выполнив:

create procedure usp_foo
as
begin
  select @@trancount;
end
go

exec usp_foo;

, который возвращает 0.

Однако, если вы пройдете через это с помощью отладчика T-SQL, @@ Транзакция фактически равна 1 внутри процедуры согласно наблюдению, хотя она возвращает 0 ...

Так что я думаю, что это побочный эффект отладчика, но затем я пишу некоторый код для его тестирования, обновляю таблицу и затем выбираю max (id) из классического:

create table nextid
(
  id int
)

insert into nextid values (0)

create procedure nextid
as
BEGIN
  UPDATE nextid set id = id + 1
  select max(id) from nextid
END

Таким образом, я ожидаю, что это выдаст дубликаты идентификаторов, поскольку они выполняются параллельно. Обновления 2 могут быть завершены до того, как 2 выберет выбор последнего идентификатора и возврат одного и того же значения, но, пытаясь ударить его с нескольких машин, я не могу его сломать. , При мониторинге блокировок и транзакций на компьютере он сообщает, что exec происходит в транзакции, и, что важно, все операторы внутри exec обрабатываются как одна единица работы / одна транзакция.

Я бы понял, если обновление было в транзакции, и это было причиной блокировки, но кажется, что блокировка сохраняется до после выбора.

Если я выполняю трассировку с помощью профилировщика, я вижу, что идентификатор транзакции предоставляется для всего выполнения оператора EXEC, и идентификатор транзакции не равен 0, как я ожидаю при выполнении ...

Может кто-нибудь объяснить мне, где я скучаю по сюжету, или я ошибаюсь, и на самом деле безопасно генерировать такие идентификаторы?

1 Ответ

1 голос
/ 01 июня 2011

Ваш тест должен давать правильные результаты, потому что вы недостаточно быстры, чтобы вызвать второй вызов между этими двумя утверждениями.Попробуйте добавить задержку, и вы увидите, что тест начнет давать сбой.

CREATE TABLE NextID
(
    ID int
)
GO

INSERT INTO NextID VALUES (0)
GO

CREATE PROC GetNextID
AS
BEGIN
    UPDATE NextID SET ID = ID + 1
    WAITFOR DELAY '00:00:05'
    SELECT Max(ID) FROM NextID
END

Введите EXEC GetNextID и введите еще один EXEC GetNextID, как только вы сможете из другого сеанса.Примерно через 5 секунд оба EXEC вернут один и тот же результат, т.е. неверное значение.Теперь измените SP на

CREATE PROC GetNextID
AS
BEGIN
    BEGIN TRAN

    UPDATE NextID SET ID = ID + 1
    WAITFOR DELAY '00:00:05'
    SELECT Max(ID) FROM NextID

    COMMIT TRAN
END

и повторите вышеуказанный тест.Вы увидите, что оба вызова вернут правильное значение.Кроме того, второй вызов (если он будет сделан как можно скорее) вернет результат примерно через 10 секунд, потому что ОБНОВЛЕНИЕ заблокировано и должно ждать 5 секунд (для первого вызова COMMIT), а затем его собственное ожидание 5 секунд.

...