Mam wyzwalacz UPDATE w tabeli, która szuka konkretnej kolumny zmieniającej się z jednej określonej wartości na dowolną inną. Gdy tak się dzieje, aktualizuje niektóre powiązane dane w innej tabeli za pomocą pojedynczej instrukcji UPDATE.
Pierwszą rzeczą, którą robi wyzwalacz, jest sprawdzenie, czy którykolwiek z zaktualizowanych wierszy zmienił wartość tej kolumny w stosunku do wartości, o której mowa. Po prostu łączy INSERTED z DELETED i porównuje wartość w tej kolumnie. Jeśli nic się nie kwalifikuje, następuje wczesne ratowanie, więc instrukcja UPDATE nie działa.
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
W takim przypadku CUSTNMBR jest kluczem podstawowym tabeli bazowej. Jeśli wykonam dużą aktualizację w tej tabeli (powiedzmy, ponad 5000 wierszy), to oświadczenie zajmie WIEK, nawet jeśli nie dotknąłem kolumny CUSTCLAS. Mogę obserwować, jak utknął na tym oświadczeniu przez kilka minut w Profiler.
Plan wykonania jest dziwaczny. Pokazuje Wstawione skanowanie z 3714 wykonaniami i ~ 18,5 milionami wierszy wyjściowych. To działa przez filtr w kolumnie CUSTCLAS. Łączy to (poprzez zagnieżdżoną pętlę) z usuniętym skanem (również filtrowanym w CUSTCLAS), który wykonuje się tylko raz i ma 5000 wierszy wyjściowych.
Jaką idiotyczną rzecz tutaj robię, żeby to spowodować? Pamiętaj, że wyzwalacz absolutnie musi poprawnie obsługiwać aktualizacje w wielu wierszach.
EDYCJA :
Próbowałem też napisać to w ten sposób (na wypadek, gdyby EXISTS robił coś nieprzyjemnego), ale wciąż jest tak samo okropne.
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
Odpowiedzi:
Możesz ocenić za pomocą jawnych
INNER MERGE JOIN
lubINNER HASH JOIN
podpowiedzi, ale biorąc pod uwagę, że prawdopodobnie używasz tych tabel ponownie później w wyzwalaczu, prawdopodobnie lepiej jest po prostu wstawić zawartość tabelinserted
ideleted
do#temp
tabel indeksowanych i wykonać to.Nie otrzymują automatycznie utworzonych dla nich przydatnych indeksów.
źródło
CUSTNMBR
celu utworzenia unikalnego indeksu klastrowego) i użyćOPTION (RECOMPILE)
podpowiedzi, aby wziąć pod uwagę liczbę wierszy, lub może po prostu użyć określonej konwencji nazewnictwa, na przykład#i_dbo_YourTable
#trigger_name_i
. Jeśli przejdę do zmiennych tabeli, będę musiał jeszcze bardziej zaśmiecać kod za pomocą jawnych instrukcji CREATE TABLE. Mamy wyzwalacze kaskadowe, ale nie wyzwalacze rekurencyjne, więc myślę, że będę bezpieczny ...OPTION (RECOMPILE)
więc liczność jest brana pod uwagę.Wiem, że odpowiedź została udzielona, ale pojawiła się jako ostatnio aktywna i natknąłem się na to również w przypadku tabel z wieloma milionami wierszy. Nie pomijając zaakceptowanej odpowiedzi, mogę przynajmniej dodać, że moje doświadczenie pokazuje, że kluczowym czynnikiem w wydajności wyzwalacza podczas wykonywania podobnych testów (sprawdzanie, czy rzeczywiście zmieniono ich wartości w jednej lub więcej kolumn), jest to, czy kolumny testowane były w rzeczywistości częścią
UPDATE
oświadczenia. Zauważyłem, że porównywanie kolumn między tabelamiinserted
ideleted
tabelami, które w rzeczywistości nie były częściąUPDATE
instrukcji, znacznie obniżyło wydajność, której inaczej nie byłoby, gdyby te pola były częściąUPDATE
instrukcja (niezależnie od faktycznej zmiany ich wartości). Dlaczego to wszystko działa (tj. Zapytanie o porównanie N pól w X wierszach) w celu ustalenia, czy coś się zmieniło, jeśli można logicznie wykluczyć możliwość zmiany którejkolwiek z tych kolumn, co oczywiście nie jest możliwe, gdyby nie były obecne wSET
klauzuliUPDATE
oświadczenia.Rozwiązaniem, które zastosowałem, było użycie funkcji UPDATE (), która działa tylko wewnątrz Triggerów. Ta wbudowana funkcja informuje, czy kolumna została podana w
UPDATE
instrukcji i może być użyta do wyjścia z wyzwalacza, jeśli kolumny, o które się martwisz, nie są częściąUPDATE
. Można tego użyć w połączeniu z a,SELECT
aby ustalić, czy te kolumny, zakładając, że są obecne wUPDATE
, mają rzeczywiste zmiany. Mam kod na górze kilku wyzwalaczy audytu, który wygląda następująco:Ta logika przejdzie do reszty wyzwalacza, jeśli:
INSERT
SET
klauzuliUPDATE
i co najmniej jedna z tych kolumn w jednym wierszu uległa zmianieNOT (UPDATE...) OR NOT EXISTS()
Może wyglądać dziwnie lub do tyłu, ale jest zaprojektowany, aby uniknąć robi toSELECT
nainserted
ideleted
stoły, jeżeli żaden z odpowiednich kolumn są częściąUPDATE
.W zależności od potrzeb funkcja COLUMNS_UPDATED () jest kolejną opcją określającą, które kolumny są częścią
UPDATE
instrukcji.źródło
UPDATE(CUSTCLAS)
i po prostu pominąć całość, jeśli fałsz (+1). Nie sądzę, że masz rację, że niezaktualizowane kolumny nie są tak łatwo dostępne w wersjach wierszy jak zaktualizowane.tempdb
zDBCC PAGE
tempdb
po prostu wypróbowałem ten skrypt , wkleiłem dane wyjściowe do notatnika i szukałem „EEEEEE”. Widzę wynik na zrzucie ekranu tutaj . Uwaga przed i po wersjach obu kolumn w obu wierszach. Mogą istnieć o wiele łatwiejsze sposoby, ale wystarczające do moich celów tutaj!tempdb
stronach obokBBBBBB
lub nie są inne długie ciągi EEEEEEDDDDDD
. Może trzeba będzie przeprowadzić dalsze dochodzenie! Chociaż może wynika to zREPLICATE
połączenia.Mógłbym spróbować przepisać używając, jeśli istnieje
źródło
http://dave.brittens.org/blog/writing-well-behaved-triggers.html
Według Dave'a powinieneś używać tabel temp lub zmiennych tabel z indeksami, ponieważ wirtualne tabele WSTAWIONO / USUWANE nie mają żadnych. Jeśli masz możliwość wyzwalania rekurencyjnego, powinieneś użyć zmiennych tabeli, aby uniknąć kolizji nazw.
Mam nadzieję, że ktoś uzna to za pomocne, ponieważ oryginalny post był jakiś czas temu ...
źródło
Poniższy kod może zwiększyć wydajność tego wyzwalacza. Nie znałem poprawnego typu danych w kolumnie [custclass], więc musisz go dostosować.
Zauważ, że możesz dołączyć do nich dodatkowe kolumny w kopiach pamięci wstawionych i usuniętych tabel, jeśli potrzebujesz ich w kodzie wyzwalacza. Klucze podstawowe w tych tabelach znacznie zwiększą wydajność łączenia podczas aktualizacji więcej niż kilku wierszy jednocześnie. Powodzenia!
źródło