У меня есть триггер UPDATE для таблицы, которая отслеживает изменение определенного столбца с одного конкретного значения на любое другое значение. Когда это происходит, он обновляет некоторые связанные данные в другой таблице с помощью одного оператора UPDATE.
Первое, что делает триггер, это проверяет, изменилось ли значение этого столбца в обновленных строках по сравнению со значением, о котором идет речь. Он просто присоединяет INSERTED к DELETED и сравнивает значение в этом столбце. Если ничего не подходит, он выдается на ранней стадии, поэтому оператор UPDATE не выполняется.
IF NOT EXISTS (
SELECT TOP 1 i.CUSTNMBR
FROM INSERTED i
INNER JOIN DELETED d
ON i.CUSTNMBR = d.CUSTNMBR
WHERE d.CUSTCLAS = 'Misc'
AND i.CUSTCLAS != 'Misc'
)
RETURN
В этом случае CUSTNMBR является первичным ключом базовой таблицы. Если я сделаю большое обновление этой таблицы (скажем, 5000+ строк), этот оператор займет AGES, даже если я не коснулся столбца CUSTCLAS. Я могу наблюдать за тем, как это заявление в течение нескольких минут отображается в Profiler.
План исполнения причудливый. Он показывает вставленное сканирование с 3714 выполнениями и ~ 18,5 миллионами выходных строк. Это проходит через фильтр в столбце CUSTCLAS. Он соединяет это (через вложенный цикл) с удаленным сканированием (также фильтруемым по CUSTCLAS), которое выполняется только один раз и имеет 5000 выходных строк.
Какую идиотскую вещь я делаю здесь, чтобы вызвать это? Обратите внимание, что триггер обязательно должен правильно обрабатывать многострочные обновления.
РЕДАКТИРОВАТЬ :
Я также попытался написать это так (на случай, если EXISTS делал что-то неприятное), но это все так же ужасно.
DECLARE @CUSTNMBR varchar(31)
SELECT TOP 1 @CUSTNMBR = i.CUSTNMBR
FROM INSERTED i
INNER JOIN DELETED d
ON i.CUSTNMBR = d.CUSTNMBR
WHERE d.CUSTCLAS = 'Misc'
AND i.CUSTCLAS != 'Misc'
IF @CUSTNMBR IS NULL
RETURN
Ответы:
Вы можете оценить использование явных
INNER MERGE JOIN
илиINNER HASH JOIN
подсказок, но, учитывая, что вы, вероятно, будете снова использовать эти таблицы позже в триггере, вам, вероятно, лучше просто вставить содержимоеinserted
иdeleted
таблицы в индексированные#temp
таблицы и покончить с этим.Они не получают полезные индексы, созданные для них автоматически.
источник
CUSTNMBR
для создания уникального кластеризованного индекса) и использоватьOPTION (RECOMPILE)
подсказку, чтобы он учитывал количество строк, или, возможно, просто использовать конкретное соглашение об именах, такое как#i_dbo_YourTable
#trigger_name_i
. Если я пойду с табличными переменными, мне придется еще больше загромождать код с помощью явных CREATE TABLE. У нас есть каскадные триггеры, но не рекурсивные, поэтому я думаю, что буду в безопасности ...OPTION (RECOMPILE)
чтобы учитывалось количество элементов .Я знаю, что на этот вопрос уже получен ответ, но он только что появился как недавно активный, и я столкнулся с этим и для таблиц с миллионами строк. Не сбрасывая со счетов принятый ответ, я могу, по крайней мере, добавить, что мой опыт показывает, что ключевым фактором в производительности триггера при выполнении аналогичных тестов (проверка того, действительно ли один или несколько столбцов были изменены значения) является ли столбец (столбцы) тестирование было на самом деле частью
UPDATE
заявления. Я обнаружил , что сравнение столбцов междуinserted
иdeleted
таблицы , которые фактически были не частьUPDATE
заявления поставить огромное сопротивление на производительности , которая была в противном случае не была , если эти поля были частьюUPDATE
заявление (независимо от того, что их значение действительно изменяется). Почему все это работает (то есть, запрос для сравнения N полей в X строках), чтобы определить, изменилось ли что-нибудь, если вы можете логически исключить возможность изменения любого из этих столбцов, что, очевидно, невозможно, если бы они не присутствовали вSET
пунктеUPDATE
заявления.Решением, которое я использовал, было использование функции UPDATE (), которая работает только внутри триггеров. Эта встроенная функция сообщает, был ли указан столбец в
UPDATE
операторе, и может использоваться для выхода из триггера, если интересующие вас столбцы не являются частьюUPDATE
. Это можно использовать вместе с a,SELECT
чтобы определить, действительно ли эти столбцы, если они присутствуют вUPDATE
, имеют реальные изменения. У меня есть код в верхней части нескольких триггеров аудита, который выглядит следующим образом:Эта логика перейдет к остальной части триггера, если:
INSERT
SET
предложении,UPDATE
и хотя бы один из этих столбцов в одной строке изменилсяNOT (UPDATE...) OR NOT EXISTS()
Может выглядеть странным или назад, но он предназначен , чтобы избежать делаетSELECT
наinserted
иdeleted
таблиц , если ни один из соответствующих столбцов не являются частьюUPDATE
.В зависимости от ваших потребностей, функция COLUMNS_UPDATED () - это еще одна опция для определения того, какие столбцы являются частью
UPDATE
оператора.источник
UPDATE(CUSTCLAS)
и просто пропустить все это, если ложь (+1). Я не думаю, что вы правы, что не обновленные столбцы не так легко доступны в версиях строк, как обновленные.tempdb
с помощьюDBCC PAGE
tempdb
я только что попробовал этот скрипт , вставил вывод в блокнот и искал "EEEEEE". Я вижу вывод на скриншоте здесь . Обратите внимание до и после версий обоих столбцов в обеих строках. Там могут быть гораздо более простые способы, но достаточно для моих целей здесь!tempdb
страницах рядом сBBBBBB
илиDDDDDD
. Возможно, придется провести еще какое-то расследование! Хотя, возможно, это связано сREPLICATE
звонком.Я мог бы попытаться переписать, используя, если существует
источник
http://dave.brittens.org/blog/writing-well-behaved-triggers.html
Согласно Дэйву, вы должны использовать временные таблицы или переменные таблиц с индексами, потому что у виртуальных таблиц INSERTED / DELETED их нет. Если у вас есть возможность рекурсивных триггеров, вам следует использовать табличные переменные, чтобы избежать конфликтов имен.
Надеюсь, кто-то найдет это полезным, так как оригинальный пост был довольно давно ...
источник
Следующий код может повысить производительность этого триггера. Я не знал правильный тип данных столбца [custclass], поэтому вам нужно настроить его.
Обратите внимание, что вы можете включить в них дополнительные столбцы в копии памяти вставленных и удаленных таблиц, если они понадобятся вам в вашем коде триггера. Первичные ключи в этих таблицах значительно повысят производительность соединения при обновлении более чем нескольких строк одновременно. Удачи!
источник