Lista produktów – jak popsuć prostą rzecz

Wstęp

W poprzednim wpisie poruszyłem kwestię tworzenia filtrów do listy produktów. Dzisiaj skupię się na samej liście, która dostarczyła mi sporo radości przy poprawianiu. Z jakiegoś powodu funkcja pobierająca dane potrzebowała od 20 do 30 sekund na odesłanie wszystkiego co było potrzebne do wyświetlenia listy. Rzut oka na mysql-slow-log pozwolił określić w czym jest problem.

Do wszystkiego, czyli do niczego

Głównym problemem okazała się klasa pobierająca dane z bazy MySQL. Przekazywane do niej były wszystkie złączenia tabel i na podstawie takiego zapytania generowana była odpowiedź. Wyglądało to mniej więcej tak:


select kolumny from produkt
left join podatek
left join produkt_kategoria
left join kategoria
left join promocje
left join opisy_w_jezyku_x
...
group by id_produktu
order by id_produktu desc
limit 0, 20

Nie wpisywałem połączeń pomiędzy tabelami, żeby niepotrzebnie nie zaśmiecać wpisu. Całe zapytanie ma jeden ekran w konsoli. Liczbę rekordów uzyskuje się na bazie tego zapytania:


select count(id_produktu) as liczba from ( powyzsze_zapytanie )

Czemu te zapytania się niedobre? Przy 1000 kategorii i 100000 produktów baza potrzebuje bardzo dużo czasu, żeby skompletować wszystkie dane i skleić ze sobą tabele produkt, produkt_kategoria i kategoria.
<h3>Bez automatu</h3>
A teraz rozwiązanie, które nie będzie sprawiało takich problemów (przynajmniej częściowo). Do liczby produktów używamy zapytania:


select count( id_produktu ) from produkt $inner $where

$inner i $where to dodatkowe warunki, które mogą się pojawić. $inner będzie zawierać dodatkowe tabele, natomiast $where warunki nałożone na kolumny w samych produktach.

Do listy produktów będą potrzebne dwa zapytania:


select id_produktu from produkt $inner $where order by id_produktu

Z powyższego zapytania wyciągamy id_produktu, sklejamy z użyciem przecinka i przekazujemy do zapytania:


select kolumny from produkt
left join podatek
left join produkt_kategoria
left join kategoria
left join promocje
left join opisy_w_jezyku_x
...
where id_produktu in ( $lista )
group by id_produktu
order by id_produktu desc
limit 0, 20

Po co dodatkowe zapytanie? Łatwiej jest wyciągnąć z samej listy produktów potrzebne numery id i przekazać je do właściwego zapytania pobierającego dane. Ewentualnie możemy zastanowić się nad zrezygnowaniem z pokazywania wszystkich kategorii, do których przypisany jest dany produkt. Odpadnie wtedy wiele połączeń z produkt_kategoria i być może group by id_produktu.

Uwagi końcowe

Jedna klasa pobierająca dane to dobra rzecz, pod warunkiem że jest wykorzystywana w taki sposób, który nie powoduje zadyszki w bazie danych. Dobrze jest też pomyśleć jakie dane będą wyświetlane na liście produktów (lub czegoś innego). Wszędzie tam, gdzie są elementy mające dowiązanie jeden do wiele, pojawią się problemy, a produkty i kategorie są wręcz idealnymi kandydatami do sprawiania kłopotów. Ani jedne, ani drugie nie są nieograniczone i prędzej czy później ich liczba spowoduje problemy z działaniem zapytań (to samo z listą zamówień). Inną rzeczą jest to, że wybierając dane do zapytania pobierającego dane nie potrzebujemy wszystkich dowiązań, które tylko mogą sprawiać problemy (zwłaszcza przy group by). Im prostsze zapytanie, tym lepiej.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

*