Вставить / обновить взаимоблокировку с помощью SQL Server

У меня есть таблица A (id int, domain nvarchar, status nvarchar) и триггер A_trigger после вставки в таблицу A. Триггер вызывает хранимую процедуру и в зависимости от результата процедуры обновляет статус во вновь вставленной строке.

Когда я запускаю его в два сеанса, у меня возникает проблема с тупиком:

Уровень изоляции: чтение совершено

INSERT заявление:

INSERT INTO dbo.TEST_TRIGGER (DOMAIN) 
VALUES ('toto')

Курок:

CREATE TRIGGER dbo.dim_trigger 
ON db.dbo.TEST_TRIGGER
AFTER INSERT
AS
    DECLARE @status nvarchar(200),
            @domain nvarchar(200),
            @trackingId int

BEGIN
    SET NOCOUNT ON;

    -- This code assumes we insert one and only one row at a time.
    SELECT @trackingId = id, @domain = domain FROM INSERTED;

    DECLARE @toCallProcName NVARCHAR(200);
    SET @toCallProcName = 'db.dbo.'+@domain+'_proc';

    EXEC @toCallProcName @status out;

    UPDATE db.dbo.TEST_TRIGGER
    SET status = @status
    WHERE id = @trackingId;
END

Я попытался:

  • выполните оператор обновления с WITH (UPDLOCK), но это не работает
  • Создание индекса для (ID) работает, но меня беспокоит это решение !!

РЕДАКТИРОВАТЬ1:

Схема таблицы:

CREATE TABLE [dbo].[TEST_TRIGGER]
(
      [DOMAIN] [NVARCHAR](200) NOT NULL,
      [ID] [BIGINT] IDENTITY(1,1) NOT NULL,
      [STATUS] [NVARCHAR](100) NULL
)

Хранимая процедура:

CREATE PROCEDURE [dbo].[toto_proc]
     @res NVARCHAR(200) OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    WAITFOR DELAY '00:00:5'
    PRINT 'This is me: '+CONVERT(VARCHAR(8), GETDATE(), 108) ;
    SET @res = 'OK'
END

Любая помощь?

Спасибо


person Ulky Igor    schedule 07.08.2018    source источник
comment
Создать индекс для каждой строки? Я не думаю, что вы понимаете, что такое индексы.   -  person Kamil Gosciminski    schedule 07.08.2018
comment
просветите меня тогда пожалуйста :) я не профи   -  person Ulky Igor    schedule 07.08.2018
comment
Покажите нам, что вы на самом деле сделали - создание индекса для каждой строки, потому что я понятия не имею, что вы пытались сделать.   -  person Kamil Gosciminski    schedule 07.08.2018
comment
CREATE INDEX i1_TEST_TRIGGER ON dbo.TEST_TRIGGER (id) Возможно, мне следовало сказать, что размер индекса будет расти очень быстро, вместо того, чтобы ... создавать индекс для каждой строки. Может я тоже не очень понимаю   -  person Ulky Igor    schedule 07.08.2018
comment
Это всего лишь один индекс, который вы создаете один раз, а не для каждой строки. Очевидно, что размер индекса растет, но очень быстро это не число, поэтому трудно дать оценку. Меня бы это не беспокоило, если вы не вставляете тысячи строк в секунду.   -  person Kamil Gosciminski    schedule 07.08.2018
comment
Пожалуйста, включите код процедуры, которую выполняет ваш триггер, и схему таблицы.   -  person Kamil Gosciminski    schedule 07.08.2018
comment
Спасибо за объяснение. Я был обеспокоен, так как это таблица отслеживания, поэтому строки будут часто вставляться в течение дня. Что касается кода процедуры, пока ничего (просто печать ... все еще в разработке), но некоторые выбирают, объединяют и вставляют в таблицы.   -  person Ulky Igor    schedule 07.08.2018
comment
Включение полного stackoverflow.com/help/mcve MCVE полезно для тех, кто хотел бы вам помочь, поэтому включите код процедуры , даже если в этом нет ничего сложного, экономит время для других, чтобы воспроизвести вашу проблему.   -  person Kamil Gosciminski    schedule 07.08.2018
comment
хорошо, спасибо @KamilG.! Я запомню это :)   -  person Ulky Igor    schedule 07.08.2018
comment
Ваш триггер имеет ГЛАВНЫЙ недостаток в том, что вы, кажется, предполагаете, что он будет вызываться один раз для каждой строки, но это не так. Триггер срабатывает один раз для каждого оператора, поэтому, если ваш INSERT, который заставляет этот триггер срабатывать, вставляет 25 строк, вы получите срабатывание триггера один раз, и псевдотаблица Inserted будет содержать 25 рядов. Какую из этих 25 строк ваш код выберет из Inserted? Это недетерминировано, вы получите одну произвольную строку, и вы будете игнорировать все остальные строки. Чтобы учесть это, вам нужно переписать свой триггер!   -  person marc_s    schedule 07.08.2018
comment
Thx @marc_s Я это прекрасно знаю :) Зная мое приложение, я не могу вставить более двух строк в каждый оператор. Но вы правы, доработаю позже;)   -  person Ulky Igor    schedule 07.08.2018


Ответы (2)


Причина вашей проблемы в том, что триггеры продолжают удерживать блокировку базовой таблицы. Это обязательно произойдет из-за нескольких сеансов и одного и того же ресурса. Отсутствие индекса в таблице вызовет сканирование таблицы каждый раз для каждого отдельного сеанса и вызовет взаимоблокировку. Применение индекса - правильный шаг, потому что таким образом ваш оператор обновления достигнет детального уровня. Так что это должно сработать. Я предлагаю дополнительный намек на RowLock в вашем заявлении об обновлении.

Важно: я предполагаю, что каждая вставка будет содержать только одно значение. В противном случае у этого триггера есть проблемы. (Как упоминалось marc_s)

person lokesh    schedule 07.08.2018

Что вам нужно сделать, так это создать индекс для вашей таблицы

пример:

CREATE INDEX IX_Test_Trigger ON dbo.TEST_TRIGGER(Id)

Прочтите это для получения дополнительной информации об индексе и его влиянии на механизм блокировки: https://www.mssqltips.com/sqlservertip/2517/using-a-clustered-index-to-solve-a-sql-server-deadlock-issue/ < / а>

person Vahid Farahmandian    schedule 07.08.2018