Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: optymalizacja zapytania
Forum PHP.pl > Forum > Bazy danych
czachor
Witam,

mam tabelę z użytkownikami, oprócz tego jest kilka tabel, do których użytkownicy wprowadzają dane. Te dane są oznaczone jako wpisane przez nich. Chcę zliczyć, ile dodali tych danych.

  1. SELECT
  2. user_id,
  3. name,
  4. COUNT(DISTINCT objects.object_id) AS count_objects,
  5. COUNT(DISTINCT auctions.auction_id) AS count_auctions,
  6. COUNT(DISTINCT turnover_id) AS count_turnover,
  7. COUNT(DISTINCT artists .artist_id) AS artist_turnover
  8. FROM users
  9. LEFT JOIN objects USING (user_id) # 8000 rekordów
  10. LEFT JOIN auctions USING (user_id) #100 rekordów
  11. LEFT JOIN turnover_history USING (user_id) #8000 rekordów
  12. LEFT JOIN artists USING (user_id) #2000 rekordów
  13. GROUP BY user_id
  14. ORDER BY name ASC


To zapytanie działa, ale bardzo długo się wykonuje. Jak powinienem je zoptymalizować? W każdej tabeli na kolumnie 'user_id' mam założony indeks. Oczywiście mogę zrobić osobne zapytanie dla każdej tabeli, ale może jest jakiś sposób optymalizacji tego.

Dodam, że czasami potrzebuję danych z konkretnego miesiąca:
  1. SELECT
  2. user_id,
  3. name,
  4. COUNT(DISTINCT objects.object_id) AS count_objects,
  5. COUNT(DISTINCT auctions.auction_id) AS count_auctions,
  6. COUNT(DISTINCT turnover_id) AS count_turnover,
  7. COUNT(DISTINCT artists .artist_id) AS artist_turnover
  8. FROM users
  9. LEFT JOIN objects USING (user_id)
  10. LEFT JOIN auctions USING (user_id)
  11. LEFT JOIN turnover_history USING (user_id)
  12. LEFT JOIN artists USING (user_id)
  13. WHERE
  14. objects.added BETWEEN '2010-03-01' AND DATE_ADD('2010-03-01', INTERVAL 1 MONTH)
  15. OR auctions.added BETWEEN '2010-03-01' AND DATE_ADD('2010-03-01', INTERVAL 1 MONTH)
  16. OR turnover_history.added BETWEEN '2010-0-013' AND DATE_ADD('2010-03-01', INTERVAL 1 MONTH)
  17. OR artists.added BETWEEN '2010-03-01' AND DATE_ADD('2010-03-01', INTERVAL 1 MONTH)
  18. GROUP BY user_id
  19. ORDER BY name ASC

...to trwa jeszcze dłużej.
samuraj85
Hmm to zależy od bazy danych ale jeśli wartości te są często pobierane to warto byłoby stworzyc osobną tabelę, do której za pomoca triggerow dodawało się dane/aktualizowało i wtedy pobranie informacji trwałoby baaardzo krótko.
czachor
To akurat stoi na MySQLu i stać musi. Jest to jakaś myśl, pytanie tylko, czy faktycznie powinno to tyle trwać - czy dobrze stworzyłem zapytanie i indeksy. BTW drugie zapytanie pobierające dane z wybranego okresu jest złe, ale pokazuje ideę.
samuraj85
Idea oki i zapytanie będzie trwało długo. Może coś by mogła zmiana kolejności poszczególnych elementów po słowach from, where ale podejrzewam że da to niezbyt wiele. do sprawdzenia tego mozna użyć polecenia explain (http://dev.mysql.com/doc/refman/5.1/en/explain.html). jednak wydaje mi się tak jak juz pisałem, że najlepszym rozwiązaniem moze być w tym przypadku stworzenie dodatkowej tabeli z której pobierane będą dane, a triggery będą je indeksowały. Taka jest moja koncepcja ale pewnie są lepsze smile.gif
6nom
Sprobuj tak (nie bede calego zapytania budowac):

  1. SELECT
  2. u.user_id,
  3. u.name,
  4. co.counter AS count_objects,
  5. ca.counter AS count_actions,
  6. ....
  7. FROM
  8. users u
  9. LEFT JOIN (SELECT COUNT(o.object_id) AS counter, o.user_id FROM objects o GROUP BY o.user_id) co ON (co.user_id = u.user_id)
  10. LEFT JOIN (SELECT COUNT(a.auction_id) AS counter, a.user_id FROM auctions a GROUP BY a.user_id) ca ON (ca.user_id = u.user_id)
  11. .....
  12. ORDER BY
  13. u.name ASC


Sprawdz czy cos przyspieszylo. Jesli tak, to dla drugiego zapytania przenies warunki na daty z where do joinow
wookieb
1) Podaj struktury tabel (silnik tabel, indeksy też) i przykładowe dane (mam nadzieję, że wiesz jak)
2) Nie robimy czegoś takiego
  1. BETWEEN '2010-03-01' AND DATE_ADD('2010-03-01', INTERVAL 1 MONTH)

Tylko w miarę możliwości
  1. BETWEEN '2010-03-01' AND '2010-04-01'

ponieważ wtedy mysql wykorzysta indeksy
3) Lepiej nie robic takich zapytań liczących i dobrze byłoby się oprzeć na buforowanie owych liczników (np dodatkowa tabela, albo dodatkowe pole w twojej tabeli users). Licznik będziesz oczywiście uaktualniać przy każdej związanej z nimi akcji albo co jakiś czas.
Jest to najwydajniejsze rozwiązanie.
mkozak
Moja propozycja 4 zapytań zamiast jednego.

Nie będzie łączenia tabel. Skleisz to sobie w PHP-ie.

Przypadkiem nie brakuje ci tam jakiś indeksów na datach, czy user_id??

Dodatkowo - jeżeli porobisz sobie indexy takie na 3 pola które cię interesują (tam z jednej tebeli user_id, pole po którym zliczasz, data) to do wykonania zapytania zostanie użyty sam index, bez udziału danych (będzie szybciej).
czachor
Dzięki wszystkim za odpowiedzi, w najbliższym czasie przetestuję Wasze propozycje i dam znać, co wyszło. Obecnie mam właśnie 4 osobne zapytania.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.