W porównaniu do łączenia z dużymi rzędami

Chcę wybrać wiersze w tabeli, gdzie klucz podstawowy znajduje się w innej tabeli. Nie jestem pewien, czy powinienem używać operatora JOIN czy in w SQL Server 2005. Czy istnieje jakaś znacząca różnica wydajności między tymi dwoma zapytaniami SQL z dużym zestawem danych (tj. miliony wierszy)?

SELECT *
FROM a
WHERE a.c IN (SELECT d FROM b)

SELECT a.*
FROM a JOIN b ON a.c = b.d
Author: macleojw, 2009-06-16

12 answers

Update:

Ten artykuł na moim blogu podsumowuje zarówno moją odpowiedź, jak i Moje komentarze do innych odpowiedzi i pokazuje rzeczywiste plany wykonania:


SELECT  *
FROM    a
WHERE   a.c IN (SELECT d FROM b)

SELECT  a.*
FROM    a
JOIN    b
ON      a.c = b.d

Te zapytania nie są równoważne. Mogą one dać różne wyniki, jeśli twoja tabela b nie jest zachowana jako kluczowa (tzn. wartości b.d nie są unikalne).

Odpowiednikiem pierwszego zapytania jest po:

SELECT  a.*
FROM    a
JOIN    (
        SELECT  DISTINCT d
        FROM    b
        ) bo
ON      a.c = bo.d

Jeśli b.d jest UNIQUE i oznaczone jako takie (z UNIQUE INDEX lub UNIQUE CONSTRAINT), to te zapytania są identyczne i najprawdopodobniej będą używać identycznych planów, ponieważ SQL Server jest wystarczająco inteligentny, aby wziąć to pod uwagę.

SQL Server może użyć jednej z następujących metod do uruchomienia tego zapytania:

  • Jeśli istnieje indeks na a.c, d jest UNIQUE i b jest relatywnie mały w porównaniu do a, wtedy warunek jest propagowany do subquery i równiny INNER JOIN jest używany (z b wiodącym)

  • Jeśli indeks jest na b.d i d nie jest UNIQUE, to warunek jest również propagowany i stosuje się LEFT SEMI JOIN. Może być również stosowany do powyższego warunku.

  • Jeśli istnieje indeks zarówno b.d, jak i a.c i są one duże, to MERGE SEMI JOIN jest używane

  • Jeśli nie ma indeksu w żadnej tabeli, to tabela hash jest zbudowana na b i używana jest HASH SEMI JOIN.

Ani z tych metod za każdym razem dokonuje ponownej oceny całego zapytania.

Zobacz ten wpis na moim blogu, aby uzyskać więcej szczegółów na temat tego, jak to działa:

Są linki do wszystkich RDBMS Z wielkiej czwórki.

 28
Author: Quassnoi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-16 22:06:05

Żadne z nich. Użyj ANSI-92 JOIN:

SELECT a.*
FROM a JOIN b a.c = b.d

Jednak najlepiej jak istnieje

SELECT a.*
FROM a
WHERE EXISTS (SELECT * FROM b WHERE a.c = b.d)

To usuwa duplikaty, które mogą być generowane przez połączenie, ale działa równie szybko, jeśli nie szybciej

 5
Author: gbn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-16 13:43:16

IN jest oceniane (i select from b Re-run) dla każdego wiersza w a, podczas gdy połączenie jest zoptymalizowane pod kątem korzystania z indeksów i innych ciekawych sztuczek przywoławczych...

W większości przypadków optymalizator prawdopodobnie byłby w stanie skonstruować JOIN z skorelowanego zapytania podrzędnego i i tak skończyłby z tym samym planem wykonania.

Edit: uprzejmie przeczytaj poniższe komentarze, aby uzyskać więcej informacji... dyskusja na temat zasadności tej odpowiedzi i faktycznej odpowiedzi na pytanie OP. =)

 4
Author: J. Steen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-16 14:11:59

Mówiąc z doświadczenia na stole z 49.000.000 wierszy polecam LEFT OUTER JOIN. Użycie w, lub istnieje trwało 5 minut, aby zakończyć, gdy lewe zewnętrzne połączenie kończy się w 1 sekundę.

SELECT a.*
FROM a LEFT OUTER JOIN b ON a.c = b.d
WHERE b.d is not null -- Given b.d is a primary Key with index

Właściwie w moim zapytaniu robię to na 9 tabel.

 4
Author: Theofanis Pantelides,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-19 11:38:35

Poza pójściem i faktycznie testowania go na dużym obszarze danych testowych dla siebie, powiedziałbym użyć łączników. Zawsze miałem lepszą wydajność przy ich użyciu w większości przypadków w porównaniu do subquery IN, i masz o wiele więcej opcji dostosowywania, jeśli chodzi o to, jak dołączyć, co jest wybrane, co nie jest, itp.

 2
Author: TheTXI,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-16 13:42:41

Są to różne zapytania o różnych wynikach. Dzięki zapytaniu IN otrzymasz 1 wiersz z tabeli 'a' za każdym razem, gdy predykat pasuje. Dzięki wewnętrznemu zapytaniu JOIN otrzymasz wiersze a * b za każdym razem, gdy warunek join pasuje. Tak więc z wartościami w a z {1,2,3} i b z {1,2,2,3} otrzymasz 1,2,2,3 z połączenia i 1,2,3 z wejścia.

EDIT-myślę, że możesz tu natknąć się na kilka odpowiedzi, które dadzą ci błędne przekonanie. Przetestuj to sam, a zobaczysz, że wszystko jest w porządku plany zapytań:

create table t1 (t1id int primary key clustered)
create table t2 (t2id int identity primary key clustered
    ,t1id int references t1(t1id)
)


insert t1 values (1)
insert t1 values (2)
insert t1 values (3)
insert t1 values (4)
insert t1 values (5)

insert t2 values (1)
insert t2 values (2)
insert t2 values (2)
insert t2 values (3)
insert t2 values (4)


select * from t1 where t1id in (select t1id from t2)
select * from t1 where exists (select 1 from t2 where t2.t1id = t1.t1id)
select t1.* from t1 join t2 on t1.t1id = t2.t1id
Pierwsze dwa plany są identyczne. Ostatni plan to zagnieżdżona pętla, ta różnica jest oczekiwana, ponieważ jak wspomniałem powyżej join ma inną semantykę.
 2
Author: ahains,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-16 13:51:05

From MSDN documentation on subquery Fundamentals:

Wiele poleceń Transact-SQL, które zapytania podrzędne include mogą być alternatywnie sformułowane jako łączniki. Inne pytania można zadawać tylko z subqueries. W Transact-SQL jest zazwyczaj nie ma różnicy wydajności pomiędzy stwierdzeniem, które zawiera subquery i semantycznie równoważne wersja, która nie. Jednak w niektóre przypadki, w których istnienie musi być sprawdzone, połączenie daje lepsze wydajność. W przeciwnym razie zagnieżdżone zapytanie musi być przetwarzane dla każdego wynik zapytania zewnętrznego w celu zapewnienia eliminacja duplikatów. W takich przypadków, podejście join dałoby lepsze wyniki.

W podanym przykładzie zagnieżdżone zapytanie musi być przetwarzane tylko raz dla każdego z zewnętrznych wyników zapytania, więc nie powinno być różnicy w wydajności. Sprawdzenie planów wykonania dla obu zapytań powinno to potwierdzić.

Uwaga: choć samo pytanie nie określiłem SQL Server 2005, odpowiedziałem tym założeniem na podstawie tagów question. Inne silniki bazodanowe (nawet różne wersje SQL Server) mogą nie optymalizować się w ten sam sposób.

 2
Author: iammichael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-16 14:08:04

Obserwuj plan realizacji dla obu typów i wyciągaj wnioski. O ile liczba rekordów zwracanych przez zapytanie podrzędne w instrukcji " IN " nie jest bardzo mała, wariant IN jest prawie na pewno wolniejszy.

 1
Author: thijs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-16 13:55:32

Użyłbym join, zakładając, że będzie o wiele szybciej niż w. Zakłada to, że istnieją klucze podstawowe zdefiniowane, oczywiście, co pozwala indeksowanie przyspieszyć rzeczy ogromnie.

 0
Author: Alan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-16 13:41:00

Ogólnie uważa się, że join byłby bardziej wydajny niż in subquery; jednak optymalizator SQL*Server zwykle nie powoduje zauważalnej różnicy w wydajności. Mimo to prawdopodobnie najlepiej jest kodować przy użyciu warunku połączenia, aby zachować spójność standardów. Ponadto, jeśli Twoje dane i Kod kiedykolwiek będą musiały zostać przeniesione w przyszłości, silnik bazy danych może nie być tak wyrozumiały(na przykład użycie join zamiast in subquery robi ogromną różnicę w MySql).

 0
Author: DBMarcos99,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-16 15:10:06

Teoria pozwoli Ci tylko na takie pytania. Pod koniec dnia będziesz chciał przetestować oba zapytania i sprawdzić, które faktycznie działają szybciej. Miałem przypadki, w których wersja JOIN zajęła ponad minutę, a wersja IN zajęła mniej niż sekundę. Miałem również przypadki, w których JOIN był rzeczywiście szybszy.

Osobiście zaczynam od wersji IN, Jeśli wiem, że nie będę potrzebował żadnych pól z tabeli zapytań podrzędnych. Jeśli zacznie działać wolno, zoptymalizuję to. Na szczęście dla duże zbiory danych, przepisanie zapytania sprawia tak zauważalną różnicę, że możesz po prostu czas go z analizatora zapytań i wiedzieć, że robisz postępy.

Powodzenia!

 0
Author: Jason Kester,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-16 18:38:31

Zawsze byłem zwolennikiem metodologii in. Ten link zawiera szczegóły testu przeprowadzonego w PostgresSQL. http://archives.postgresql.org/pgsql-performance/2005-02/msg00327.php

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-04 18:18:41