Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 Подскажите, почему "виснет" запрос в триггере  [new]
pokibor
Member

Откуда:
Сообщений: 25
В одном проекте (до сих пор на SQL Server 2005) обнаружил очень странное поведение в триггере. Возможно, проблема лечится каким-то обновлением?
Естественно, реальные запросы сложнее, но я упрощу их для иллюстрации проблемы, благо проверено - на таких запросах проблема возникает.
Итак, есть таблица, допустим "Main"; важные для нас поля - ID и некое FLD. К ней привязана таблица "R" с внешним ключём Main_ID.
На таблице R висит такой триггер:
FOR INSERT, UPDATE, DELETE
AS BEGIN
	UPDATE MAIN SET FLD = '...' WHERE ID IN (SELECT Main_ID FROM INSERTED UNION SELECT Main_ID FROM DELETED);
END

Ну, идея понятна: обновить (на самом деле пересчитать функцией, но проблема и так возникает) записи главной таблицы, "затронутые" изменением связанной (то есть если в связанной поменялся Main_ID, то нужно обновить и те записи, с которыми была связь, и те, с которыми она появилась).
Так вот, при большом количестве записей этот запрос то ли виснет, то ли просто отжирает кучу ресурсов... не ясно.

Если переписать триггер так:
FOR INSERT, UPDATE, DELETE
AS BEGIN
	UPDATE MAIN SET FLD = '...' WHERE ID IN (SELECT Main_ID FROM INSERTED);
	UPDATE MAIN SET FLD = '...' WHERE ID IN (SELECT Main_ID FROM DELETED);
END

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

Самое интересное, что это касается только выборки из INSERTED/DELETED внешнего ключа. Если, например, написать так:
UPDATE MAIN SET FLD = '...' WHERE ID IN (SELECT ID FROM INSERTED UNION SELECT ID FROM DELETED);
(то есть выбрать собственный id, что бессмысленно с точки зрения логики) - всё работает. Если просто написать в триггере
FOR INSERT, UPDATE, DELETE
AS BEGIN
	SELECT Main_ID FROM INSERTED UNION SELECT Main_ID FROM DELETED;
END

тоже всё работает. А вот сделать update с каким-то совмещением выборки из DELETED и INSERTED - никак, причём попытка вынести подзапросы в WITH ничего не дала.
Работает даже соединение DELETED с INSERTED:
UPDATE MAIN SET FLD = '...' WHERE ID IN (SELECT i.Main_ID FROM INSERTED i JOIN DELETED d ON i.Main_ID = d.Main_ID);

Но опять же, с точки зрения логики это бессмысленно и мне нужно другое.
13 фев 16, 09:23    [18812341]     Ответить | Цитировать Сообщить модератору
 Re: Подскажите, почему "виснет" запрос в триггере  [new]
pokibor
Member

Откуда:
Сообщений: 25
Сработал такой извращённый вариант:
UPDATE a SET FLD = '...'
FROM MAIN a 
LEFT OUTER JOIN INSERTED i ON i.Main_ID = a.ID
LEFT OUTER JOIN DELETED d ON d.Main_ID = a.ID
WHERE i.ID IS NOT NULL OR d.ID IS NOT NULL;

Видимо, проблема с планом исполнения запроса, но я не знаю, как посмотреть его для запросов в триггере...
13 фев 16, 09:33    [18812357]     Ответить | Цитировать Сообщить модератору
 Re: Подскажите, почему "виснет" запрос в триггере  [new]
invm
Member

Откуда: Москва
Сообщений: 9836
pokibor
но я не знаю, как посмотреть его для запросов в триггере...
Профайлером.
13 фев 16, 09:50    [18812372]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить