Utworzyłem polecenie SQL, które używa INNER JOIN na 9 stołach, w każdym razie to polecenie zajmuje bardzo dużo czasu (ponad pięć minut). Więc mój lud zaproponował mi zmianę INNER JOIN na LEFT JOIN, ponieważ wydajność LEFT JOIN jest lepsza, pomimo tego, co wiem. Po jego zmianie szybkość zapytania znacznie się poprawiła.
Chciałbym wiedzieć, dlaczego LEFT JOIN jest szybszy niż INNER JOIN?
Moje polecenie SQL wygląda jak poniżej:
SELECT * FROM A INNER JOIN B ON ... INNER JOIN C ON ... INNER JOIN D
i tak dalej
Aktualizacja: To jest skrót mojego schematu.
FROM sidisaleshdrmly a -- NOT HAVE PK AND FK
INNER JOIN sidisalesdetmly b -- THIS TABLE ALSO HAVE NO PK AND FK
ON a.CompanyCd = b.CompanyCd
AND a.SPRNo = b.SPRNo
AND a.SuffixNo = b.SuffixNo
AND a.dnno = b.dnno
INNER JOIN exFSlipDet h -- PK = CompanyCd, FSlipNo, FSlipSuffix, FSlipLine
ON a.CompanyCd = h.CompanyCd
AND a.sprno = h.AcctSPRNo
INNER JOIN exFSlipHdr c -- PK = CompanyCd, FSlipNo, FSlipSuffix
ON c.CompanyCd = h.CompanyCd
AND c.FSlipNo = h.FSlipNo
AND c.FSlipSuffix = h.FSlipSuffix
INNER JOIN coMappingExpParty d -- NO PK AND FK
ON c.CompanyCd = d.CompanyCd
AND c.CountryCd = d.CountryCd
INNER JOIN coProduct e -- PK = CompanyCd, ProductSalesCd
ON b.CompanyCd = e.CompanyCd
AND b.ProductSalesCd = e.ProductSalesCd
LEFT JOIN coUOM i -- PK = UOMId
ON h.UOMId = i.UOMId
INNER JOIN coProductOldInformation j -- PK = CompanyCd, BFStatus, SpecCd
ON a.CompanyCd = j.CompanyCd
AND b.BFStatus = j.BFStatus
AND b.ProductSalesCd = j.ProductSalesCd
INNER JOIN coProductGroup1 g1 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup1Cd
ON e.ProductGroup1Cd = g1.ProductGroup1Cd
INNER JOIN coProductGroup2 g2 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup2Cd
ON e.ProductGroup1Cd = g2.ProductGroup1Cd
sql
sql-server
performance
Anonimowy
źródło
źródło
coUOM
? Jeśli nie, możesz użyć połączenia częściowo. Jeśli tak, będziesz mógł skorzystaćUNION
z alternatywy. Publikowanie tylkoFROM
klauzuli jest tutaj nieodpowiednimi informacjami.Odpowiedzi:
A nie
LEFT JOIN
jest absolutnie szybszy niżINNER JOIN
. W rzeczywistości jest wolniejszy; z definicji połączenie zewnętrzne (LEFT JOIN
lubRIGHT JOIN
) musi wykonać całą pracęINNER JOIN
plus dodatkową pracę polegającą na przedłużeniu wartości null wyników. Oczekuje się również, że zwróci więcej wierszy, co dodatkowo zwiększy całkowity czas wykonania po prostu ze względu na większy rozmiar zestawu wyników.(I nawet jeśli
LEFT JOIN
były szybsze w określonych sytuacjach z powodu trudnej do wyobrażenia zbieżności czynników, nie jest to funkcjonalnie równoważne zINNER JOIN
, więc nie można po prostu zastąpić wszystkich wystąpień jednego z nich drugim!)Najprawdopodobniej problemy z wydajnością leżą gdzie indziej, na przykład brak prawidłowego indeksowania klucza kandydującego lub klucza obcego. 9 stołów to całkiem sporo, więc spowolnienie może dosłownie być niemal wszędzie. Jeśli opublikujesz swój schemat, możemy podać więcej szczegółów.
Edytować:
Zastanawiając się nad tym, mógłbym pomyśleć o jednej okoliczności, w której a
LEFT JOIN
może być szybsze niż anINNER JOIN
, i wtedy:Rozważ ten przykład:
Jeśli uruchomisz to i przejrzysz plan wykonania, zobaczysz, że
INNER JOIN
zapytanie rzeczywiście kosztuje więcej niżLEFT JOIN
, ponieważ spełnia dwa powyższe kryteria. Jest tak, ponieważ SQL Server chce dopasować skrót dlaINNER JOIN
, ale zagnieżdżone pętle dlaLEFT JOIN
; pierwsze jest zwykle znacznie szybsze, ale ponieważ liczba wierszy jest tak mała i nie ma indeksu do użycia, operacja mieszania okazuje się być najdroższą częścią zapytania.Możesz zobaczyć ten sam efekt, pisząc program w swoim ulubionym języku programowania do wykonywania dużej liczby wyszukiwań na liście zawierającej 5 elementów, w porównaniu do tabeli mieszającej z 5 elementami. Ze względu na rozmiar wersja tabeli skrótów jest wolniejsza. Ale zwiększ go do 50 elementów lub 5000 elementów, a wersja listy spowalnia do indeksowania, ponieważ jest to O (N) vs. O (1) dla tablicy mieszającej.
Ale zmień to zapytanie na
ID
kolumnę zamiast,Name
a zobaczysz zupełnie inną historię. W takim przypadku wykonuje zagnieżdżone pętle dla obu zapytań, aleINNER JOIN
wersja jest w stanie zastąpić jedno ze skanów indeksu klastrowego wyszukiwaniem - co oznacza, że będzie to dosłownie o rząd wielkości szybsze z dużą liczbą wierszy.Wniosek jest mniej więcej tym, o czym wspominałem kilka akapitów powyżej; prawie na pewno jest to problem z indeksowaniem lub pokryciem indeksu, prawdopodobnie w połączeniu z jedną lub kilkoma bardzo małymi tabelami. Są to jedyne okoliczności, w których SQL Server może czasami wybrać gorszy plan wykonania dla
INNER JOIN
niż niżLEFT JOIN
.źródło
Jest jeden ważny scenariusz, który może doprowadzić do tego, że połączenie zewnętrzne jest szybsze niż połączenie wewnętrzne, które nie zostało jeszcze omówione.
Podczas korzystania z zewnętrznego połączenia optymalizator zawsze może upuścić zewnętrzną połączoną tabelę z planu wykonania, jeśli kolumny łączenia są PK tabeli zewnętrznej i żadna z kolumn tabeli zewnętrznej nie jest przywoływana poza samym złączem zewnętrznym. Na przykład
SELECT A.* FROM A LEFT OUTER JOIN B ON A.KEY=B.KEY
B.KEY to PK dla B. Zarówno Oracle (myślę, że korzystałem z wersji 10), jak i Sql Server (użyłem 2008 R2) tabeli przycinania B z planu wykonania.To samo niekoniecznie jest prawdą dla połączenia wewnętrznego:
SELECT A.* FROM A INNER JOIN B ON A.KEY=B.KEY
może, ale nie musi wymagać B w planie wykonania, w zależności od istniejących ograniczeń.Jeśli A.KEY jest zerowalnym kluczem obcym odwołującym się do B.KEY, optymalizator nie może usunąć B z planu, ponieważ musi potwierdzić, że wiersz B istnieje dla każdego wiersza A.
Jeśli A.KEY jest obowiązkowym kluczem obcym odwołującym się do B.KEY, optymalizator może usunąć B z planu, ponieważ ograniczenia gwarantują istnienie wiersza. Ale to, że optymalizator może usunąć tabelę z planu, nie oznacza, że tak będzie. SQL Server 2008 R2 NIE usuwa B z planu. Wyrocznia 10 Zrzuca B z planu. W tym przypadku łatwo jest zobaczyć, jak zewnętrzne połączenie wykona wewnętrzne połączenie na SQL Server.
Jest to trywialny przykład i niepraktyczny w przypadku samodzielnego zapytania. Po co dołączać do stołu, jeśli nie potrzebujesz?
Ale może to być bardzo ważne przy projektowaniu widoków. Często budowany jest widok „rób wszystko”, który łączy wszystko, czego użytkownik może potrzebować w związku z centralnym stołem. (Zwłaszcza jeśli naiwni użytkownicy wykonują zapytania ad-hoc, które nie rozumieją modelu relacyjnego) Widok może zawierać wszystkie odpowiednie kolumny z wielu tabel. Ale użytkownicy końcowi mogą uzyskiwać dostęp do kolumn tylko z podzestawu tabel w widoku. Jeśli tabele są połączone zewnętrznymi złączeniami, optymalizator może (i robi) usunąć niepotrzebne tabele z planu.
Bardzo ważne jest, aby upewnić się, że widok przy użyciu złączeń zewnętrznych daje prawidłowe wyniki. Jak powiedział Aaronaught - nie możesz na ślepo zastąpić DOŁĄCZENIA ZEWNĘTRZNEGO do DOŁĄCZENIA WEWNĘTRZNEGO i oczekiwać takich samych rezultatów. Ale są chwile, kiedy może to być przydatne ze względu na wydajność podczas korzystania z widoków.
Ostatnia uwaga - nie testowałem wpływu na wydajność w świetle powyższego, ale teoretycznie wydaje się, że powinieneś być w stanie bezpiecznie zastąpić WEJŚCIE WEWNĘTRZNE JOINEM ZEWNĘTRZNYM, jeśli dodasz również warunek <KLUCZ_KOPU> NIE JEST NULL do klauzuli where.
źródło
Jeśli wszystko działa tak, jak nie powinno, ALE wszyscy wiemy, że wszystko nie działa tak, jak powinno, szczególnie jeśli chodzi o optymalizator zapytań, buforowanie planu zapytań i statystyki.
Najpierw zasugerowałbym przebudowanie indeksu i statystyk, a następnie wyczyszczenie pamięci podręcznej planu zapytań, aby upewnić się, że to nie popsuło. Jednak napotkałem problemy, nawet gdy to zrobiono.
Doświadczyłem niektórych przypadków, w których lewe połączenie było szybsze niż połączenie wewnętrzne.
Podstawowy powód jest następujący: jeśli masz dwie tabele i dołączasz do kolumny z indeksem (w obu tabelach). Sprzężenie wewnętrzne da ten sam wynik bez względu na to, czy zapętlisz wpisy w indeksie w tabeli 1 i dopasujesz z indeksem w tabeli 2, tak jakbyś zrobił odwrotnie: Zapętlić wpisy w indeksie w tabeli 2 i dopasować z indeksem w tabeli pierwszej. Problem polega na tym, że gdy masz mylące statystyki, optymalizator zapytań użyje statystyk indeksu, aby znaleźć tabelę z najmniej pasującymi pozycjami (na podstawie innych kryteriów). Jeśli masz dwie tabele z 1 milionem w każdej, w pierwszej tabeli masz 10 pasujących wierszy, aw drugiej tabeli 100 000 pasujących wierszy. Najlepszym sposobem byłoby wykonanie skanowania indeksu w tabeli pierwszej i dopasowanie 10 razy w tabeli drugiej. Odwrotnym byłoby skanowanie indeksu, które zapętla ponad 100 000 wierszy i próbuje dopasować 100 000 razy, a tylko 10 się powiedzie. Więc jeśli statystyki nie są poprawne, optymalizator może wybrać niewłaściwą tabelę i indeks, który ma być zapętlony.
Jeśli optymalizator zdecyduje się zoptymalizować lewe łączenie w kolejności, w jakiej jest zapisane, będzie działać lepiej niż łączenie wewnętrzne.
ALE, optymalizator może również zoptymalizować lewe łączenie suboptymalne jako lewe pół złączenie. Aby to zrobić, wybierz ten, który chcesz, możesz użyć podpowiedzi na temat wymuszania kolejności.
źródło
Wypróbuj oba zapytania (jedno z łączeniem wewnętrznym i lewym) z
OPTION (FORCE ORDER)
na końcu i opublikuj wyniki.OPTION (FORCE ORDER)
jest wskazówką dotyczącą zapytania, która zmusza optymalizator do zbudowania planu wykonania przy użyciu kolejności łączenia podanej w zapytaniu.Jeśli
INNER JOIN
zacznie działać tak szybko jakLEFT JOIN
to, to dlatego, że:INNER JOIN
s kolejność łączenia nie ma znaczenia. Daje to optymalizatorowi kwerendę swobodę zamawiania połączeń według własnego uznania, więc problem może zależeć od optymalizatora.LEFT JOIN
przypadku nie jest tak, ponieważ zmiana kolejności łączenia spowoduje zmianę wyników zapytania. Oznacza to, że silnik musi przestrzegać kolejności łączenia podanej w zapytaniu, która może być lepsza niż zoptymalizowana.Nie wiem, czy to odpowiada na twoje pytanie, ale byłem kiedyś w projekcie, który zawierał bardzo złożone zapytania wykonujące obliczenia, które całkowicie zawiodły optymalizator. Mieliśmy przypadki, w których a
FORCE ORDER
skróciłoby czas wykonywania zapytania z 5 minut do 10 sekund.źródło
Dokonałem szeregu porównań między lewymi złączami zewnętrznymi i wewnętrznymi i nie byłem w stanie znaleźć stałej różnicy. Istnieje wiele zmiennych. Pracuję nad bazą danych raportów z tysiącami tabel, wiele z dużą liczbą pól, wiele zmian w czasie (wersje dostawcy i lokalny przepływ pracy). Nie można utworzyć wszystkich kombinacji indeksów obejmujących, aby zaspokoić potrzeby tak szerokiej gamy zapytań i obsługiwać dane historyczne. Widziałem, że zapytania wewnętrzne zabijają wydajność serwera, ponieważ dwie duże (miliony do dziesiątków milionów wierszy) tabele są połączone wewnętrznie, oba pobierają dużą liczbę pól i nie istnieje indeks pokrycia.
Największy problem nie wydaje się jednak przemawiać w powyższych dyskusjach. Być może Twoja baza danych jest dobrze zaprojektowana z wyzwalaczami i dobrze zaprojektowanym przetwarzaniem transakcji, aby zapewnić dobre dane. Mój często ma wartości NULL tam, gdzie nie są oczekiwane. Tak, definicje tabel mogą wymuszać wartości zerowe, ale nie jest to opcja w moim środowisku.
Pytanie brzmi: czy projektujesz zapytanie tylko pod kątem szybkości, wyższego priorytetu dla przetwarzania transakcji, który uruchamia ten sam kod tysiące razy na minutę. Czy też dążysz do dokładności, jaką zapewni lewe połączenie zewnętrzne. Pamiętaj, że sprzężenia wewnętrzne muszą znajdować dopasowania po obu stronach, więc nieoczekiwany NULL nie tylko usunie dane z dwóch tabel, ale prawdopodobnie całe rzędy informacji. I dzieje się tak ładnie, bez komunikatów o błędach.
Możesz być bardzo szybki, ponieważ uzyskanie 90% potrzebnych danych i nie odkrywanie, że połączenia wewnętrzne po cichu usunęły informacje. Czasami połączenia wewnętrzne mogą być szybsze, ale nie sądzę, aby ktokolwiek przyjmował takie założenie, chyba że zapoznał się z planem wykonania. Szybkość jest ważna, ale ważniejsza jest dokładność.
źródło
Prawdopodobnie problemy z wydajnością wynikają z liczby wykonywanych połączeń i tego, czy kolumny, do których dołączasz, mają indeksy, czy nie.
W najgorszym przypadku możesz łatwo wykonać 9 skanów całego stołu dla każdego połączenia.
źródło
Połączenia zewnętrzne mogą oferować doskonałą wydajność, gdy są używane w widokach.
Załóżmy, że masz zapytanie obejmujące widok, a ten widok składa się z 10 tabel połączonych ze sobą. Załóżmy, że zapytanie używa tylko kolumn z 3 z tych 10 tabel.
Gdyby te 10 stołów zostało połączonych wewnętrznie , optymalizator zapytań musiałby połączyć je wszystkie, nawet jeśli samo zapytanie nie potrzebuje 7 z 10 tabel. Wynika to z faktu, że połączenia wewnętrzne same w sobie mogą filtrować dane, co czyni je niezbędnymi do obliczeń.
Gdyby te 10 tabel zostały połączone zewnętrznie sobą , wówczas optymalizator zapytań w rzeczywistości połączyłby tylko te, które były konieczne: 3 z 10 z nich w tym przypadku. Jest tak, ponieważ same sprzężenia nie filtrują już danych, a zatem nieużywane sprzężenia można pominąć.
Źródło: http://www.sqlservercentral.com/blogs/sql_coach/2010/07/29/poor-little-misunderstood-views/
źródło
Znalazłem coś interesującego na serwerze SQL, gdy sprawdzam, czy połączenia wewnętrzne są szybsze niż połączenia lewe.
Jeśli nie uwzględnisz elementów lewej tabeli złączonej, w instrukcji select lewe złączenie będzie szybsze niż to samo zapytanie z łączeniem wewnętrznym.
Jeśli uwzględnisz lewą połączoną tabelę w instrukcji select, złączenie wewnętrzne z tym samym zapytaniem będzie równe lub szybsze niż lewe złączenie.
źródło
Z moich porównań wynika, że mają dokładnie taki sam plan wykonania. Istnieją trzy scenariusze:
Jeśli i kiedy zwracają te same wyniki, mają tę samą prędkość. Musimy jednak pamiętać, że nie są to te same zapytania i że LEFT JOIN zwróci więcej wyników (gdy niektóre warunki ON nie zostaną spełnione) - dlatego zwykle jest wolniejszy.
Gdy główna tabela (pierwsza nie stała w planie wykonania) ma warunek ograniczający (GDZIE id =?), A odpowiadający jej warunek WŁĄCZONY ma wartość NULL, tabela „prawa” nie jest dołączana - wtedy LEFT JOIN jest szybszy.
Jak omówiono w punkcie 1, zwykle INNER JOIN jest bardziej restrykcyjny i zwraca mniej wyników, a zatem jest szybszy.
Oba używają (tych samych) wskaźników.
źródło