Czy lepiej jest podzielić duże zapytanie na wiele mniejszych?

13

Są sytuacje, które wymagają naprawdę dużego zapytania łączącego kilka tabel razem z instrukcjami sub select w celu uzyskania pożądanych rezultatów.

Moje pytanie brzmi: czy powinniśmy rozważyć użycie wielu mniejszych zapytań i przenieść operacje logiczne do warstwy aplikacji, wysyłając zapytanie do bazy danych w więcej niż jednym wywołaniu, czy lepiej mieć je wszystkie za jednym razem?
Na przykład rozważ następujące zapytanie:

SELECT *
FROM   `users`
WHERE  `user_id` IN (SELECT f2.`friend_user_id`
                     FROM   `friends` AS f1
                            INNER JOIN `friends` AS f2
                              ON f1.`friend_user_id` = f2.`user_id`
                     WHERE  f2.`is_page` = 0
                            AND f1.`user_id` = "%1$d"
                            AND f2.`friend_user_id` != "%1$d"
                            AND f2.`friend_user_id` NOT IN (SELECT `friend_user_id`
                                                            FROM   `friends`
                                                            WHERE  `user_id` = "%1$d"))
       AND `user_id` NOT IN (SELECT `user_id`
                             FROM   `friend_requests`
                             WHERE  `friend_user_id` = "%1$d")
       AND `user_image` IS NOT NULL
ORDER  BY RAND() 
LIMIT %2$d

Jak najlepiej to zrobić?

Hamed Momeni
źródło

Odpowiedzi:

14

Nie będę się zgadzać na duże i skomplikowane zapytania z datagod tutaj. Widzę je tylko jako problemy, jeśli są zdezorganizowane. Pod względem wydajności są one prawie zawsze lepsze, ponieważ planista ma znacznie większą swobodę w wyszukiwaniu informacji. Jednak duże zapytania muszą być pisane z myślą o łatwości konserwacji. Ogólnie rzecz biorąc, odkryłem, że ten prosty, dobrze skonstruowany SQL jest łatwy do debugowania, nawet jeśli jedno zapytanie trwa ponad 200 wierszy. Wynika to z tego, że zazwyczaj masz całkiem niezłe pojęcie z jakim problemem masz do czynienia, więc w zapytaniu jest tylko kilka obszarów, które musisz sprawdzić.

Problemy z konserwacją, IME, pojawiają się, gdy struktura SQL się psuje. Długie, złożone zapytania w podselekcjach pogarszają czytelność i rozwiązywanie problemów, podobnie jak widoki wbudowane, i obu tych należy unikać w długich zapytaniach. Zamiast tego użyj WIDOKÓW, jeśli możesz (zwróć uwagę, że korzystasz z MySQL, widoki nie działają tak dobrze, ale robią to w większości innych baz danych) i używaj wspólnych wyrażeń tabelowych tam, gdzie one nie działają (MySQL nie obsługuje tych btw).

Długie złożone zapytania działają całkiem dobrze zarówno w przypadku łatwości konserwacji, jak i wydajności, w których klauzule where są proste, i gdzie robisz tyle, ile możesz z łączeniami zamiast podselekcji. Celem jest sprawienie, aby „rekordy się nie wyświetlały” zapewniło kilka bardzo konkretnych miejsc w zapytaniu do sprawdzenia (czy zostanie ono upuszczone w złączeniu, czy odfiltrowane w klauzuli where?), A więc zespół obsługi technicznej może faktycznie utrzymywać rzeczy.

Jeśli chodzi o skalowalność, pamiętaj, że im większa elastyczność ma planista, to też dobrze ...

Edycja: Wspomniałeś, że to MySQL, więc widoki prawdopodobnie nie będą tak dobrze działać, a CTE nie wchodzi w rachubę. Dodatkowo podany przykład nie jest szczególnie długi ani skomplikowany, więc nie stanowi to problemu.

Chris Travers
źródło
Uwaga: miałem zapytania (nie w MySQL, ale nadal ...), które były na tyle długie i złożone, że wygenerowane plany zapytań nie były optymalne. W takich przypadkach rzeczywiście można uzyskać szybsze wyniki, dzieląc jedno niezwykle złożone zapytanie na dwa mniej złożone zapytania. To powiedziawszy, jest to rzadkie i na ogół napiszę złożone zapytanie i dowiem się, czy jest jakiś problem, zamiast dzielenia zapytania na mniejsze części z wyprzedzeniem.
RDFozz
8

Jako ktoś, kto musi wspierać / oczyszczać te duże i skomplikowane zapytania, powiedziałbym, że o wiele lepiej jest podzielić je na kilka małych, łatwych do zrozumienia części. Z punktu widzenia wydajności niekoniecznie jest to lepsze, ale przynajmniej dajesz SQLowi większą szansę na wymyślenie dobrego planu zapytań.

Ułatw życie ludziom, którzy podążają za tobą, a będą mówić o tobie dobre rzeczy. Uciskaj ich, a przeklną cię.

datagod
źródło
2
wadą szeregu prostych zapytań jest jednak to, że stan zmienia się znacznie w nich, co sprawia, że ​​ogólne debugowanie aplikacji jest bardziej złożone. Oznacza to, że można często debugować duże zapytania SQL jako drzewa, ale kod aplikacji jest debugowany przez instrukcję sprawdzającą, jak zmienia się stan instrukcji. Prawdziwe problemy mają związek z faktem, że podselekcje i widoki wbudowane są również własnymi drzewami .....
Chris Travers
W moim przypadku jedynym, który musi zarządzać bazą danych i kodem, jestem ja. I przede wszystkim moje pytanie dotyczyło punktu wydajności zapytania.
Hamed Momeni
Musicie rzucić okiem na sposób, w jaki piszę moje duże procesy wsadowe. Podziel rzeczy na proste zapytania, bardzo łatwe do odczytania. Jestem stronniczy, ponieważ zapytania, które próbuję uporządkować, mają rutynowo ponad 1000 linii.
datagod
5

Moje 2 centy za 2 słowa kluczowe wydajność zapytania i skalowalność:

Wydajność zapytań: Równoległość programu SQL Server wykonuje już bardzo dobrą robotę, dzieląc zapytania na wyszukiwania wielowątkowe, więc nie jestem pewien, ile ulepszenia wydajności zapytań zobaczysz, robiąc to dla programu SQL Server. Będziesz musiał spojrzeć na plan wykonania, aby zobaczyć, jaki stopień równoległości uzyskujesz, gdy go wykonasz, i porównać wyniki w obie strony. Jeśli w końcu będziesz musiał użyć wskazówki zapytania, aby uzyskać taką samą lub lepszą wydajność, wtedy IMO nie jest tego warte, ponieważ wskazówka zapytania może nie być optymalna później.

Skalowalność: czytanie zapytań może być łatwiejsze, jak stwierdził datagod, a podział na osobne zapytania ma sens, jeśli możesz używać nowych zapytań również w innych obszarach, ale jeśli nie zamierzasz ich używać również do innych połączeń, będzie jeszcze więcej przechowywanych procesów do zarządzania dla jednego zadania, a IMO nie przyczyni się do skalowalności.

Ali Razeghi
źródło
2
RE: Odwołania do „SQL Server”, chociaż OP nie określił żadnego konkretnego RDBMS. Podejrzewam, że są na MySQL od tylnych tyknięć iLIMIT
Martin Smith
@MartinSmith Podejrzewasz poprawnie. To jest MySQL.
Hamed Momeni
2

Czasami nie ma innego wyjścia, jak podzielić duże / złożone zapytanie na małe zapytania. Najlepszym sposobem ustalenia tego byłoby użycie EXPLAINinstrukcji z SELECTinstrukcją. Liczba śladów / skanów, które wykona db w celu pobrania danych, jest iloczynem wartości „wierszy” zwróconych przez EXPLAINzapytanie. W naszym przypadku mieliśmy zapytanie łączące 10 tabel. Dla konkretnego rekordu ślad wyniósł 409 mln blogów na naszej bazie danych i zwiększył wykorzystanie procesora na naszym serwerze DB o ponad 300%. Byliśmy w stanie odzyskać te same informacje, dzieląc zapytania znacznie szybciej.

Krótko mówiąc, w niektórych przypadkach rozdzielenie złożonego / dużego zapytania ma sens, ale w innych może prowadzić do wielu problemów z wydajnością lub utrzymaniem i należy to rozpatrywać indywidualnie.

użytkownik140665
źródło