Co jest bardziej wydajne, klauzula where lub łączenie z milionami tabel wierszy?


17

Prowadzimy stronę internetową, która ma 250 mln wierszy w jednej tabeli, aw drugiej tabeli, do której dołączamy, w przypadku większości zapytań ma nieco mniej niż 15 mln wierszy.

Przykładowe struktury:

MasterTable (Id, UserId, Created, Updated...) -- 15MM Rows
DetailsTable (Id, MasterId, SomeColumn...) -- 250MM Rows
UserTable (Id, Role, Created, UserName...) -- 12K Rows

Regularnie musimy wykonać kilka zapytań w stosunku do wszystkich tych tabel. Jednym z nich jest zbieranie statystyk dla darmowych użytkowników (~ 10 000 darmowych użytkowników).

Select Count(1) from DetailsTable dt 
join MasterTable mt on mt.Id = dt.MasterId 
join UserTable ut on ut.Id = mt.UserId 
where ut.Role is null and mt.created between @date1 and @date2

Problem w tym, że to zapytanie będzie czasem działało bardzo cholernie z powodu faktu, że łączenia odbywają się na długo przed tym, gdzie.

Czy w takim przypadku rozsądniej byłoby użyć gdzieś zamiast złączeń, a może where column in(...)?


1
Jaka baza danych i wersja?
Leigh Riffel

2
próbowałeś na dwa sposoby?
gbn

Gdyby to był Oracle, stworzyłbym indeks oparty na funkcjach dla UserTable na NVL2 (Rola, NULL, ID), ale wygląda to na inną bazę danych.
Leigh Riffel

Odpowiedzi:


20

W przypadku nowoczesnych RDBMS nie ma różnicy między „jawnym JOIN” a „JOIN-in-the-WHERE” (jeśli wszystkie JOINS są INNER) pod względem wydajności i planu zapytań.

Wyraźna składnia JOIN jest jaśniejsza i mniej dwuznaczna (patrz linki poniżej)

Teraz JOIN-before-WHERE to przetwarzanie logiczne, a nie przetwarzanie rzeczywiste, a nowoczesne optymalizatory są wystarczająco sprytne, aby to zrealizować.

Twoim problemem tutaj jest najprawdopodobniej indeksowanie.

Pokaż nam wszystkie indeksy i klucze w tych tabelach. I plany zapytań

Uwaga: to pytanie byłoby już blisko StackOverflow za to, że jest duplikatem ... COUNT (1) vs COUNT (*) to kolejny mit, który został zniszczony.


2
To nie zawsze jest prawdą, że nie ma różnicy pomiędzy joini whereklauzula. Cały czas optymalizuję długo działające zapytania, a czasem zapytania korzystające z whereklauzuli działają lepiej niż te, które są używane joinnawet 70 razy. Gdyby to było takie proste, życie byłoby wszystkimi tęczami i jednorożcami. I nie chodzi tu o jakiś starożytny, niejasny silnik - teraz patrzę na 70-krotną przewagę whereklauzuli w SQL 2012.
ajeh

Co więcej, często obserwuję dokładnie te same plany z obu podejść i izoluję zapytania, które działają dokładnie tak samo, ale kiedy wherezapytanie klauzulowe działa w dużej partii, powinno być częścią tego zadania , znacznie przewyższa joinzapytanie. Zapytania SQL nie wykonują się w próżni - wpływa na nich reszta obciążenia serwera, a często wherezapytania klauzulowe wypadają całkiem dobrze, co jest uciążliwe, ponieważ joinskładnia jest rzeczywiście o wiele czystsza.
ajeh

3
@ajeh: Sugerowałbym, że twoje doświadczenie jest bardzo nietypowe. Masz większe problemy z zapytaniami, jeśli masz różnice x70: to takie proste
gbn

5

Musisz całkowicie przefiltrować zapytanie

Spróbuj wykonać klauzule WHERE wcześniej, a JOIN później

Select Count(1) from DetailsTable dt
join (Select UserId,Id FROM MasterTable where
created between @date1 and @date2) mt on mt.Id = dt.MasterId 
join (Select Id FROM UserTable WHERE Role is NULL) ut
on ut.Id = mt.UserId;

Nawet jeśli uruchomisz plan EXPLAIN dla tego refaktoryzowanego zapytania i wygląda to gorzej niż oryginał, wypróbuj go mimo to. Tabele tymczasowe utworzone wewnętrznie będą wykonywać połączenia kartezjańskie, ale te tabele są mniejsze do pracy.

Ten pomysł pochodzi z tego filmu na YouTube .

Wypróbowałem zasady z filmu w bardzo złożonym pytaniu w StackOverflow i dostałem nagrodę za 200 punktów.

@gbn wspomniał o upewnieniu się, że masz odpowiednie indeksy. W takim przypadku zindeksuj utworzoną kolumnę w MasterTable.

Spróbuj !!!

AKTUALIZACJA 2011-06-24 22:31 EDT

Powinieneś uruchomić następujące zapytania:

SELECT COUNT(1) AllRoles FROM UserTable;
SELECT COUNT(1) NullRoles FROM UserTable WHERE Role is NULL;

Jeśli NullRoles X 20 <AllRoles (innymi słowy, jeśli NullRoles jest mniejszy niż 5% wierszy tabeli), należy utworzyć nieunikalny indeks roli w UserTable. W przeciwnym razie wystarczyłaby pełna tabela UserTable, ponieważ Optymalizator zapytań może wykluczyć użycie indeksu.

AKTUALIZACJA 2011-06-25 12:40 EDT

Ponieważ jestem DBA MySQL, moja metoda robienia rzeczy wymaga nieufności MySQL Query Optimizer poprzez pozytywny pesymizm i bycie konserwatywnym. Dlatego spróbuję ponownie przeformułować zapytanie lub utworzyć niezbędne indeksy pokrywające, aby wyprzedzić ukryte złe nawyki Optymalizatora zapytań MySQL. Odpowiedź @ gbn wydaje się bardziej kompletna, ponieważ SQL Server może mieć więcej „zdrowego rozsądku” podczas oceny zapytań.


0

Mieliśmy tabelę [Szczegóły] około 75 milionów wierszy; tabela [Master] około 400 000 wierszy i powiązana tabela [Item], która miała 7 wierszy - zawsze i na zawsze. Przechowywał mały zestaw „numerów artykułów” (1-7) i modelował formę papierową, z której miliony drukowano i dystrybuowano co miesiąc. Najszybszym zapytaniem było to, o którym najprawdopodobniej nie pomyślałeś w pierwszej kolejności, używając połączenia kartezjańskiego. IIRC, to było coś takiego:

SELECT m.order_id, i.line_nr, d.Item_amt
FROM Master m, Item i 
INNER JOIN Detail d ON m.order_id = d.order_id

Mimo że istnieje logiczne połączenie „id” między [Przedmiotem] a [Szczegółem], DOŁĄCZ DO SKRZYDŁA działał lepiej niż DOŁĄCZ DO WEWNĘTRZNEGO.

RDBMS to Teradata z technologią MPP, a IDR to schemat indeksowania. Tabela 7 wierszy nie miała indeksu, ponieważ SKANOWANIE TABELI zawsze działało najlepiej.

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.