Próbuję użyć MERGE
instrukcji, aby wstawić lub usunąć wiersze z tabeli, ale chcę działać tylko na podzestawie tych wierszy. Dokumentacja MERGE
zawiera dość mocno sformułowane ostrzeżenie:
Ważne jest, aby określić tylko kolumny z tabeli docelowej, które są używane do dopasowywania celów. To znaczy określ kolumny z tabeli docelowej, które są porównywane z odpowiednią kolumną tabeli źródłowej. Nie należy poprawiać wydajności kwerendy, odfiltrowując wiersze w tabeli docelowej w klauzuli ON, na przykład określając AND NOT target_table.column_x = wartość. Może to spowodować nieoczekiwane i niepoprawne wyniki.
ale wydaje się, że właśnie to muszę zrobić, aby wykonać swoją MERGE
pracę.
Dane, które posiadam, są standardową tabelą łączenia wielu elementów w kategorie (np. Które elementy są zawarte w jakich kategoriach):
CategoryId ItemId
========== ======
1 1
1 2
1 3
2 1
2 3
3 5
3 6
4 5
Muszę skutecznie zastąpić wszystkie wiersze w określonej kategorii nową listą elementów. Moja pierwsza próba zrobienia tego wygląda następująco:
MERGE INTO CategoryItem AS TARGET
USING (
SELECT ItemId FROM SomeExternalDataSource WHERE CategoryId = 2
) AS SOURCE
ON SOURCE.ItemId = TARGET.ItemId AND TARGET.CategoryId = 2
WHEN NOT MATCHED BY TARGET THEN
INSERT ( CategoryId, ItemId )
VALUES ( 2, ItemId )
WHEN NOT MATCHED BY SOURCE AND TARGET.CategoryId = 2 THEN
DELETE ;
To wydaje się działać w moich testach, ale robię dokładnie to, co MSDN wyraźnie ostrzega mnie nie do zrobienia. Niepokoi mnie to, że później napotkam nieoczekiwane problemy, ale nie widzę innego sposobu, aby wywrzeć MERGE
wpływ tylko na wiersze o określonej wartości pola ( CategoryId = 2
) i zignorować wiersze z innych kategorii.
Czy istnieje „bardziej poprawny” sposób na osiągnięcie tego samego wyniku? A jakie są „nieoczekiwane lub niepoprawne wyniki”, o których ostrzega mnie MSDN?
źródło
Odpowiedzi:
MERGE
Oświadczenie ma złożoną składnię i jeszcze bardziej skomplikowane wdrożenie, ale przede wszystkim chodzi o to, aby połączyć dwie tabele, filtr do wierszy, które muszą być zmienione (wstawione, zaktualizowane lub usunięte), a następnie wykonać żądane zmiany. Biorąc pod uwagę następujące przykładowe dane:Cel
Źródło
Pożądanym rezultatem jest zastąpienie danych w celu danymi ze źródła, ale tylko dla
CategoryId = 2
. Zgodnie z opisemMERGE
podanym powyżej powinniśmy napisać zapytanie, które łączy źródło i cel tylko na kluczach i filtrować wiersze tylko wWHEN
klauzulach:Daje to następujące wyniki:
Plan wykonania jest:
Zauważ, że obie tabele są w pełni skanowane. Możemy uważać to za nieefektywne, ponieważ tylko wiersze, na które
CategoryId = 2
wpłynie to w tabeli docelowej. W tym miejscu pojawiają się ostrzeżenia w Books Online. Jedna błędna próba optymalizacji, by dotknąć tylko niezbędnych wierszy w celu, to:Logika w
ON
klauzuli jest stosowana jako część sprzężenia. W takim przypadku złączenie jest pełnym zewnętrznym złączeniem ( dlaczego w tej książce online ). Zastosowanie sprawdzania kategorii 2 w wierszach docelowych jako część złączenia zewnętrznego ostatecznie powoduje usunięcie wierszy z inną wartością (ponieważ nie pasują do źródła):Podstawową przyczyną jest ten sam powód, dla którego predykaty zachowują się inaczej w zewnętrznej
ON
klauzuli łączenia , niż mają to miejsce, jeśli są określone w tejWHERE
klauzuli.MERGE
Składnia (i realizacja przyłączenia w zależności od określonych klauzul) tylko utrudnić, aby zobaczyć, że tak jest.Poradnictwo w Books Online (rozszerzony w Optymalizacja wydajności wejściu) daje wskazówki, które zapewnią prawidłową semantycznej jest wyrażona za pomocą
MERGE
składni, bez użytkownik koniecznie konieczności zrozumieć wszystkie szczegóły implementacji lub konto dla sposobu, w jaki optymalizator może legalnie przestawiać rzeczy ze względu na wydajność wykonania.Dokumentacja oferuje trzy potencjalne sposoby wdrożenia wczesnego filtrowania:
Określenie warunku filtrowania w
WHEN
klauzuli gwarantuje prawidłowe wyniki, ale może oznaczać, że więcej wierszy jest odczytywanych i przetwarzanych z tabel źródłowej i docelowej, niż jest to absolutnie konieczne (jak widać w pierwszym przykładzie).Aktualizacja za pomocą widoku zawierającego warunek filtrowania gwarantuje również prawidłowe wyniki (ponieważ zmienione wiersze muszą być dostępne do aktualizacji za pośrednictwem widoku), ale wymaga to dedykowanego widoku i takiego, który spełnia nieparzyste warunki aktualizacji widoków.
Stosowanie wspólnego wyrażenia tabelowego niesie podobne ryzyko do dodawania predykatów do
ON
klauzuli, ale z nieco innych powodów. W wielu przypadkach będzie to bezpieczne, ale wymaga to specjalistycznej analizy planu wykonania, aby to potwierdzić (i obszerne testy praktyczne). Na przykład:Daje to prawidłowe wyniki (nie powtarzane) z bardziej optymalnym planem:
Plan odczytuje tylko wiersze dla kategorii 2 z tabeli docelowej. Może to być ważnym czynnikiem wpływającym na wydajność, jeśli tabela docelowa jest duża, ale zbyt łatwo jest to zrobić źle, używając
MERGE
składni.Czasami łatwiej jest pisać
MERGE
jako osobne operacje DML. Takie podejście może nawet działać lepiej niż jedenMERGE
, co często zaskakuje ludzi.źródło