2015-11-25 1 views
1

У меня есть таблица SQL Agent_Events, которая регистрирует «задания» из-за отсутствия лучшего термина, о котором сообщают пользователи нашей системы. Запуск начнется и, какое-то неопределенное время спустя, закончится. Мы хотим сохранить запись всех системных заданий, поэтому у нас есть триггер, который, когда строка удаляется из таблицы Agent_Events, получает запись и вставляется в таблицу report_Agent_Events запись задания с завершением задания. по текущему времени. Это триггер:Триггеры T-SQL, выполняющие вставку, вызывающие тупик

ALTER TRIGGER [dbo].[tAgentEvents] 
ON [dbo].[Agent_Events] 
AFTER DELETE 
AS 
BEGIN 
SET NOCOUNT ON 
    DECLARE @callUID nvarchar(10) 
    SELECT @callUID = [raw_call_ucid] FROM DELETED 
    DECLARE @callID bigint 
    SELECT TOP 1 @callID = [ID] FROM report_ExtensionCalls WHERE UCID = @callUID ORDER BY [time_finished] DESC 

    INSERT INTO [report_agent_Events] 
     ([Agent_Name],[time_started],[time_ended],[duration],[clientStatus],[agentStatus],[Call_ID],[Reference_ID]) 
     ( SELECT 
       [Agent_Name],[time_started],GETDATE(),DATEDIFF(s,[time_started],GETDATE()),[clientStatus],[agentStatus],@callID as Call_ID,[Reference_ID] 
        FROM 
       DELETED 
     ) 
END 

То, что мы обнаружили в последнее время является то, что при наличии большого объема деятельности по системе, несколько рабочих мест может закончиться одновременно. Когда это произойдет, то триггер будет завершаться с ошибкой следующей тупиковой:

Procedure tAgentEvents: 
Transaction (Process ID) was deadlocked on lock resources with another process and has been chosen as the deadlock victim RSS 

Похоже, заявление INSERT в триггере конфликтуют с замком наложенного же оператором INSERT в параллельном триггере. Есть ли что-нибудь, что я могу сделать, чтобы избежать этой проблемы? Это кажется мне очень простым триггером.

Заранее спасибо.

+2

Ваш триггер требует полной перезаписи независимо от тупика. Ваш триггер предполагает, что только одна строка будет удалена. Ваш запрос должен быть установлен на основе, потому что в SQL Server триггеры вызывается один раз за операцию, а не один раз в строке. –

+0

Ах, хорошо, я считаю, что это было написано так, потому что на самом деле строки удаляются хранимой процедурой, которая гарантирует, что только одна строка удаляется сразу. Будет ли это проблематично? – Raiden616

+2

Да. Что происходит, когда есть проблема, и кому-то нужно удалить 10 строк из-за ошибки в системе. Ваш триггер не будет работать правильно. Их легко установить на основе набора. В этом случае не имеет смысла, чтобы 1 столбец был скаляром из удаленных, но остальные из них поступали непосредственно из удаленной таблицы. Это должно быть простым соединением между вашей базовой таблицей, удаленным и report_ExtensionCalls. –

ответ

0

Просто обновил триггер, чтобы он работал за один раз, с несколькими удаленными линиями и с несколькими совпадениями в report_ExtensionCalls.

ALTER TRIGGER [dbo].[tAgentEvents] 
ON [dbo].[Agent_Events] 
AFTER DELETE 
AS 
BEGIN 
    WITH CTE AS (
     SELECT d.Id AS DeletedId 
      , rec.[ID] 
      , ROW_NUMBER() OVER (PARTITION BY rec.UCID ORDER BY rec.[time_finished] DESC) AS RowN 
     FROM DELETED AS d 
     JOIN report_ExtensionCalls AS rec ON d.[raw_call_ucid] = rec.UCID 
    ) 
    INSERT INTO [report_agent_Events] 
     ([Agent_Name],[time_started],[time_ended],[duration],[clientStatus],[agentStatus],[Call_ID],[Reference_ID]) 
     ( SELECT 
       d.[Agent_Name] 
       ,d.[time_started] 
       ,GETDATE() 
       ,DATEDIFF(s,d.[time_started],GETDATE()) 
       ,d.[clientStatus] 
       ,d.[agentStatus] 
       ,a.[ID] as Call_ID 
       ,d.[Reference_ID] 
        FROM DELETED AS d 
        LEFT JOIN CTE AS a ON d.Id = a.DeletedId AND a.RowN = 1 
     ) 
END 

Надеется, что это помогает

+0

Да, это работает намного лучше - см. Мой комментарий к вопросу. Спасибо за помощь! – Raiden616

+0

Да. Просто обновил его, чтобы учесть, есть ли больше одного совпадения в report_ExtensionCalls. –

+0

Следует упомянуть - не всегда будет значение для [raw_call_ucid] .. оно иногда передается как NULL. Просто измените gthe первый JOIN на LEFT JOIN, это исправить, верно? – Raiden616

1

Это должно учитывать все бизнес-правила в исходном триггере, но без скалярных переменных.

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

INSERT INTO [report_agent_Events] 
(
    [Agent_Name] 
    , [time_started] 
    , [time_ended] 
    , [duration] 
    , [clientStatus] 
    , [agentStatus] 
    , [Call_ID] 
    , [Reference_ID] 
) 
SELECT [Agent_Name] 
    , [time_started] 
    , GETDATE() 
    , DATEDIFF(SECOND, [time_started], GETDATE()) 
    , [clientStatus] 
    , [agentStatus] 
    , raw_call_ucid 
    , [Reference_ID] 
FROM DELETED d 
join 
(
    select top 1 ID as CallID 
    from report_ExtensionCalls ec 
    where ec.UCID = d.raw_call_ucid 
    order by [time_finished] DESC 
) e on e.CallID = d.raw_call_ucid 
Смежные вопросы