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(...)
?
join
best-practices
Jeremy Boyd
źródło
źródło
Odpowiedzi:
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.
źródło
join
iwhere
klauzula. Cały czas optymalizuję długo działające zapytania, a czasem zapytania korzystające zwhere
klauzuli działają lepiej niż te, które są używanejoin
nawet 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ęwhere
klauzuli w SQL 2012.where
zapytanie klauzulowe działa w dużej partii, powinno być częścią tego zadania , znacznie przewyższajoin
zapytanie. Zapytania SQL nie wykonują się w próżni - wpływa na nich reszta obciążenia serwera, a częstowhere
zapytania klauzulowe wypadają całkiem dobrze, co jest uciążliwe, ponieważjoin
składnia jest rzeczywiście o wiele czystsza.Musisz całkowicie przefiltrować zapytanie
Spróbuj wykonać klauzule WHERE wcześniej, a JOIN później
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:
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ń.
źródło
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:
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.
źródło