SQL łączy z podzapytaniami SQL (wydajność)?


110

Chciałbym wiedzieć, czy mam zapytanie typu join podobne do tego -

Select E.Id,E.Name from Employee E join Dept D on E.DeptId=D.Id

i podzapytanie podobne do tego -

Select E.Id,E.Name from Employee Where DeptId in (Select Id from Dept)

Kiedy rozważam wydajność, które z dwóch zapytań byłoby szybsze i dlaczego ?

Czy jest też czas, kiedy powinienem preferować jedno nad drugim?

Przepraszam, jeśli jest to zbyt trywialne i pytane wcześniej, ale jestem zdezorientowany. Byłoby również wspaniale, gdybyście mogli zasugerować mi narzędzia, których powinienem użyć do pomiaru wydajności dwóch zapytań. Wielkie dzięki!


5
@Lucero, to pytanie jest oznaczone tagiem sql-server-2008, gdzie wspomniany post jest oznaczony tagiem MySql. Możesz wywnioskować, że odpowiedzi będą takie same. Optymalizacja wydajności jest wykonywana inaczej na dwóch RDBMS.
Francois Botha

Odpowiedzi:


48

SPODZIEWAŁEM SIĘ, że pierwsze zapytanie będzie szybsze, głównie dlatego, że masz równoważność i jawne JOIN. Z mojego doświadczenia INwynika, że ​​jest to bardzo powolny operator, ponieważ SQL zwykle ocenia go jako serię WHEREklauzul oddzielonych znakiem „LUB” ( WHERE x=Y OR x=Z OR...).

Podobnie jak w przypadku ALL THINGS SQL, przebieg może się różnić. Szybkość będzie w dużym stopniu zależała od indeksów (czy masz indeksy na obu kolumnach ID? To bardzo pomoże ...), między innymi.

Jedynym PRAWDZIWYM sposobem na stwierdzenie ze 100% pewnością, który jest szybszy, jest włączenie śledzenia wydajności (szczególnie przydatne są statystyki IO) i uruchomienie obu. Pamiętaj, aby wyczyścić pamięć podręczną między biegami!


16
Mam poważne wątpliwości co do tej odpowiedzi, ponieważ większość DBMS, na pewno SQL Server 2008 i późniejsze, tłumaczy pojedyncze podzapytanie o identyfikator (nieskorelowane, co oznacza: brak odwołań do wielu zewnętrznych kolumn zapytań) na stosunkowo szybkie półzłączenie. Ponadto, jak wcześniej zauważono w innej odpowiedzi, pierwsze, prawdziwe sprzężenie zwróci wiersz dla KAŻDEGO wystąpienia pasującego identyfikatora w dziale - nie ma to znaczenia dla unikalnego identyfikatora, ale da tony duplikatów w innym miejscu. Sortowanie ich za pomocą DISTINCT lub GROUP BY będzie kolejnym, dużym obciążeniem wydajnościowym. Sprawdź plany wykonania w SQL Server Management Studio!
Erik Hart

2
Klauzula IN jako odpowiednik OR ma zastosowanie do list parametrów / wartości, ale nie do podzapytań, które są zwykle traktowane jak łączenia.
Erik Hart

42

Cóż, uważam, że jest to pytanie „Stare, ale złote”. Odpowiedź brzmi: „To zależy!”. Przedstawienia są tak delikatnym tematem, że byłoby zbyt głupio powiedzieć: „Nigdy nie używaj podzapytań, zawsze łącz”. W poniższych linkach znajdziesz kilka podstawowych sprawdzonych metod, które okazały się bardzo pomocne:

Mam stół z 50000 elementów, wynik, którego szukałem to 739 elementów.

Na początku moje zapytanie brzmiało:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND p.anno = (
    SELECT MAX(p2.anno) 
    FROM prodotto p2 
    WHERE p2.fixedId = p.fixedId 
)

a wykonanie zajęło 7,9 sekundy.

W końcu moje pytanie brzmi:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND (p.fixedId, p.anno) IN
(
    SELECT p2.fixedId, MAX(p2.anno)
    FROM prodotto p2
    WHERE p.azienda_id = p2.azienda_id
    GROUP BY p2.fixedId
)

i zajęło to 0,0256s

Dobry SQL, dobry.


3
Ciekawe, czy możesz wyjaśnić, jak dodanie funkcji GROUP BY naprawiło to?
cozos

6
Tymczasowa tabela wygenerowana przez podzapytanie była mniejsza. Dlatego wykonanie jest szybsze, ponieważ jest mniej danych do zaewidencjonowania.
Sirmyself,

2
Myślę, że w pierwszym zapytaniu masz wspólną zmienną między zewnętrznym zapytaniem a podzapytaniem, więc dla każdego wiersza w głównym zapytaniu podzapytanie jest wykonywane, ale w drugim podzapytanie jest wykonywane tylko raz, co poprawia wydajność.
Ali Faradjpour

1
Serwer SQL oraz MySql i ... Sql (z wyjątkiem NoSql) są tak podobne w infrastrukturze. Pod spodem mamy coś w rodzaju mechanizmu optymalizacji zapytań, który konwertuje klauzule IN (...) na łączenie (jeśli to możliwe). Ale jeśli masz grupę według dobrze zindeksowanej kolumny (na podstawie jej liczności), będzie to znacznie szybsze. Więc to naprawdę zależy od sytuacji.
Alix

10

Zacznij przeglądać plany wykonania, aby zobaczyć różnice w sposobie ich interpretacji przez serwer SQl. Możesz także użyć Profilera, aby faktycznie wielokrotnie uruchamiać zapytania i uzyskać różnicę.

Nie spodziewałbym się, że będą one tak strasznie różne, gdzie można uzyskać rzeczywisty, duży wzrost wydajności, używając złączeń zamiast podzapytań, gdy używasz skorelowanych podzapytań.

EXISTS jest często lepsze niż którekolwiek z tych dwóch, a kiedy mówisz o złączeniach lewostronnych, w których chcesz, aby wszystkie rekordy nie znajdowały się w tabeli łączenia po lewej stronie, to NIE ISTNIEJE jest często znacznie lepszym wyborem.


9

Wydajność zależy od ilości danych wykonywanych na ...

Jeśli jest mniej danych, około 20k. JOIN działa lepiej.

Jeśli dane są bardziej zbliżone do 100k +, wtedy IN działa lepiej.

Jeśli nie potrzebujesz danych z drugiej tabeli, IN jest dobre, ale zawsze lepiej jest wybrać EXISTS.

Wszystkie te kryteria przetestowałem i tabele mają odpowiednie indeksy.


4

Wydajność powinna być taka sama; o wiele ważniejsze jest zastosowanie odpowiednich indeksów i grupowania w tabelach (istnieje kilka dobrych zasobów na ten temat).

(Zredagowano w celu odzwierciedlenia zaktualizowanego pytania)


4

Te dwa zapytania mogą nie być semantycznie równoważne. Jeśli pracownik pracuje dla więcej niż jednego działu (jest to możliwe w przedsiębiorstwie, dla którego pracuję; co prawda oznaczałoby to, że twoja tabela nie jest w pełni znormalizowana), to pierwsze zapytanie zwróci zduplikowane wiersze, podczas gdy drugie zapytanie nie. Aby zapytania były równoważne w tym przypadku, DISTINCTsłowo kluczowe musiałoby zostać dodane do SELECTklauzuli, co może mieć wpływ na wydajność.

Zauważ, że istnieje praktyczna zasada projektowania, która mówi, że tabela powinna modelować jednostkę / klasę lub związek między jednostkami / klasami, ale nie oba. Dlatego proponuję stworzyć trzecią tabelę, powiedzmy OrgChart, do modelowania relacji między pracownikami a działami.


4

Wiem, że to stary post, ale myślę, że jest to bardzo ważny temat, szczególnie w dzisiejszych czasach, gdy mamy ponad 10 milionów rekordów i mówimy o terabajtach danych.

Zwrócę również uwagę na następujące spostrzeżenia. Mam około 45 milionów rekordów w mojej tabeli ([dane]) i około 300 rekordów w mojej tabeli [cats]. Mam obszerne indeksowanie dla wszystkich zapytań, o których będę mówić.

Rozważ przykład 1:

UPDATE d set category = c.categoryname
FROM [data] d
JOIN [cats] c on c.id = d.catid

w porównaniu z przykładem 2:

UPDATE d set category = (SELECT TOP(1) c.categoryname FROM [cats] c where c.id = d.catid)
FROM [data] d

Przykład 1 trwał około 23 minut. Przykład 2 zajął około 5 minut.

Więc doszedłbym do wniosku, że pod-zapytanie w tym przypadku jest znacznie szybsze. Oczywiście pamiętaj, że używam dysków SSD M.2 obsługujących I / O przy 1 GB / s (to bajty, a nie bity), więc moje indeksy też są naprawdę szybkie. Więc może to wpłynąć również na prędkość w twoich okolicznościach

Jeśli jest to jednorazowe czyszczenie danych, prawdopodobnie najlepiej zostawić je i zakończyć. Używam TOP (10000) i widzę, ile czasu to zajmuje, i mnożę przez liczbę rekordów, zanim trafię na duże zapytanie.

Jeśli optymalizujesz produkcyjne bazy danych, zdecydowanie sugerowałbym wstępne przetwarzanie danych, tj. Użycie wyzwalaczy lub pośrednika zadań do asynchronicznej aktualizacji rekordów, aby dostęp w czasie rzeczywistym pobierał dane statyczne.


0

Możesz skorzystać z planu wyjaśniania, aby uzyskać obiektywną odpowiedź.

W przypadku Twojego problemu filtr Exists prawdopodobnie działałby najszybciej.


2
„Filtr Exists prawdopodobnie działałby najszybciej” - myślę, że prawdopodobnie nie, chociaż ostateczna odpowiedź wymagałaby sprawdzenia rzeczywistych danych. Filtry Exists będą prawdopodobnie szybsze, gdy istnieje wiele wierszy z tymi samymi wartościami wyszukiwania - więc filtr istniejący może działać szybciej, jeśli zapytanie sprawdza, czy inni pracownicy zostali zarejestrowani z tego samego działu, ale prawdopodobnie nie podczas wyszukiwania w odniesieniu do działu stół.

Czy działałby wolniej w tym ostatnim scenariuszu?
Snekse

Zależy to od optymalizatora - w pewnych okolicznościach może, ale normalnie oczekiwałbym bardzo podobnej wydajności.
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.