Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Pomoc w optymalizacji
Forum PHP.pl > Forum > Bazy danych > MySQL
Wasper
Czesc,
mam zapytanie, ktore jeszcze nie zawiera 100% okreslonych pozycji, a juz generuje sie ponad 4s.
Baza jest w miare przyzwoicie zindeksowana. Chcialbym zasiegnac Waszej opinii, czy jest mozliwosc ulepszenia samego zapytania.

Zapytanie:
  1. SELECT
  2. tfirm.name fname,
  3. tfirm.city fcity,
  4. tfirm_navo.navo_code ncode,
  5. tperson.name pname,
  6. tperson.family pfamily,
  7. psr.name psrname,
  8. psr.family psrfamily,
  9. um_str.user_id um,
  10. tperson.detail_process,
  11. tperson.mail_info,
  12. tperson.own_business,
  13. telefon.telefon ismobile,
  14. mail.mail ismail,
  15. logowania.suma log_sum,
  16. logowania.last_log ostatni_log,
  17. zamowienia.liczba zam_value
  18. FROM
  19. tperson
  20. INNER JOIN tuser_firm ON tuser_firm.user_id = tperson.user_id
  21. INNER JOIN tfirm ON tfirm.id = tuser_firm.firm_id
  22. INNER JOIN tfirm_navo ON tfirm_navo.firm_id = tfirm.id
  23. INNER JOIN (
  24. SELECT count( tuser_log.id ) suma, IF(max(tuser_log.timestamp) IS NULL, '0000-00-00 00:00:00', max(tuser_log.timestamp)) last_log, tperson.user_id
  25. FROM tperson
  26. LEFT JOIN tuser_log ON tperson.user_id = tuser_log.user_id
  27. AND log_type =1 WHERE tperson.role=7
  28. GROUP BY tperson.user_id
  29. )logowania ON logowania.user_id = tperson.user_id
  30. INNER JOIN tfirm_psr ON tfirm_psr.firm_code=tfirm_navo.navo_code
  31. INNER JOIN tteren_user ON tteren_user.teren_id=tfirm_psr.psr_field
  32. INNER JOIN tuser_navo ON tuser_navo.navo_id=tteren_user.navo_user_id
  33. INNER JOIN tperson psr ON psr.user_id=tuser_navo.user_id
  34. INNER JOIN tstructure um_str ON um_str.child=tuser_navo.navo_id
  35. INNER JOIN
  36. (SELECT tperson.user_id uid, IF((tperson_mobile.mobile='' OR tperson_mobile.mobile IS NULL), 0, 1) telefon
  37. FROM tperson LEFT JOIN tperson_mobile ON tperson.user_id=tperson_mobile.user_id WHERE tperson.role=7) telefon ON telefon.uid=tperson.user_id
  38. INNER JOIN
  39. (SELECT tperson.user_id uid, IF((tperson_mail.mail='' OR tperson_mail.mail IS NULL), 0, 1) mail
  40. FROM tperson LEFT JOIN tperson_mail ON tperson.user_id=tperson_mail.user_id WHERE tperson.role=7) mail ON mail.uid=tperson.user_id
  41. INNER JOIN
  42. (SELECT tperson.user_id uid, count(torder.id) liczba FROM tperson LEFT JOIN torder ON tperson.user_id=torder.user_id GROUP BY tperson.user_id) zamowienia ON zamowienia.uid=tperson.user_id
  43. WHERE
  44. tperson.role=7 AND tfirm.STATUS=1


probowalem pozbyc sie podzapytan i stworzylem cos takiego

  1. SELECT
  2. tfirm.name fname,
  3. tfirm.city fcity,
  4. tfirm_navo.navo_code ncode,
  5. tperson.name pname,
  6. tperson.family pfamily,
  7. psr.name psrname,
  8. psr.family psrfamily,
  9. um_str.user_id um,
  10. tperson.detail_process,
  11. tperson.mail_info,
  12. tperson.own_business,
  13.  
  14. count( tuser_log.id ),
  15. IF(max(tuser_log.timestamp) IS NULL, '0000-00-00 00:00:00', max(tuser_log.timestamp)),
  16. IF((tperson_mobile.mobile='' OR tperson_mobile.mobile IS NULL), 0, 1),
  17. IF((tperson_mail.mail='' OR tperson_mail.mail IS NULL), 0, 1),
  18. count(torder.id)
  19.  
  20. FROM
  21. tperson
  22. INNER JOIN tuser_firm ON tuser_firm.user_id = tperson.user_id
  23. INNER JOIN tfirm ON tfirm.id = tuser_firm.firm_id
  24. INNER JOIN tfirm_navo ON tfirm_navo.firm_id = tfirm.id
  25. LEFT JOIN tuser_log ON tuser_log.user_id=tperson.user_id AND tuser_log.log_type=1
  26. LEFT JOIN tperson_mobile ON tperson_mobile.user_id=tperson.user_id AND tperson_mobile.STATUS=1
  27. LEFT JOIN tperson_mail ON tperson_mail.user_id=tperson.user_id AND tperson_mail.STATUS=1
  28. LEFT JOIN torder ON torder.user_id=tperson.user_id
  29.  
  30. INNER JOIN tfirm_psr ON tfirm_psr.firm_code=tfirm_navo.navo_code
  31. INNER JOIN tteren_user ON tteren_user.teren_id=tfirm_psr.psr_field
  32. INNER JOIN tuser_navo ON tuser_navo.navo_id=tteren_user.navo_user_id
  33. INNER JOIN tperson psr ON psr.user_id=tuser_navo.user_id
  34. INNER JOIN tstructure um_str ON um_str.child=tuser_navo.navo_id
  35.  
  36. WHERE
  37. tperson.role=7 AND tfirm.STATUS=1 AND
  38. HAVING count( tuser_log.id )=0
  39. GROUP BY tperson.user_id, tuser_log.user_id, torder.user_id


dziala o wiele szybciej
ale pojawia sie kwestia filtrowania wg countow , gdyz wrzucajac do warunku where przykladowo count( tuser_log.id )=1 wyrzuca "invalid use of group function".
Moze to jakas moja niewiedza... dlatego chcialbym zasiegnac Waszej opinii.
A gory dzieki za pomoc.

edit.
zapomnialem dodac, ze ify sa wstawione po to, zeby uniknac wartosci NULL w zapytaniu (kwestia uzycia kwerendy w frameworku.. nie ma filtrow obslugujacych nulle)

Wasp

update. zapomnialem o having smile.gif moze to z racji nieprzespanej nocy wink.gif
alegorn
uff, ale to wielkie.
nie mam czasu analizowac calosci, ale odniose sie do tego kawalka:


  1. IF(max(tuser_log.timestamp) IS NULL, '0000-00-00 00:00:00', max(tuser_log.timestamp)),
  2. IF((tperson_mobile.mobile='' OR tperson_mobile.mobile IS NULL), 0, 1),
  3. IF((tperson_mail.mail='' OR tperson_mail.mail IS NULL), 0, 1),



przedefiniuj te kolumny na NOT NULL, i ustaw wartosc domyslna.
ominiesz w ten sposob kilka operacji czyli czysty zysk. [zwlaszcza na OR]

ogolnie stosowanie wartosci NULL w bazie danych oznacza ze najprawdopodobniej projekt jest nieprzemyslany / baza nie jest znormalizowana

przemysl, czy optymalizacja tylko zapytania da ci efekt.
pokaz jakiegos explaina, to tez troche nam podpowie.

co do indeksow, radze uwazac.
ja np wczoraj wywalilem wczoraj primary, na zastepczym indeksie (czyli popularny autoinc).
po jego wywaleniu, i zalozeniu zwyklego indeksu na inna kolumne - zapytanie z czasu ~~4sec spadlo do 0,8
po kolejnej rozkminie i przebudowie tabeli - uzyskalem efekt na poziomie ~~0,02

z ~~4 sec na ~~0,02 wiec optymalizacja zapytania o conajmniej 200%

pisze to dlatego - ze chce zwrocic uwage, iz nie uzyskalbym az takiej redukcji czasu zmieniajac tylko kwerende.
(oczywiscie najwazniejszym kryterium tabeli byl odczyt.)
Wasper
Co do if`ow to null wynika z left joina.

ale hmmm... zastanowiles mnie ta wypowiedzia. Nie jest to baza skonstruowana przeze mnie i niestety mam ograniczona mozliwosc zmiany struktury. Ale z tego co widze, to indeksy primary sa wszedzie ustawione na kolumne ID, niezaleznie od relacji. W wielu tabelach sa one poprostu nieistotne.
Mowisz, ze mozna uzyskac az taka roznice?
alegorn
zalezy od tego czy te indeksy wykorzystujesz, oraz jakiego rodzaju jest to zapytanie.

pamietaj, ze baza danych, na innodb domyslnie sortuje wiersze wg glownego indeksu.

jesli wybierasz wg innego warunku (niz id autoinc.), a do tego musisz sortowac wynik, sprawa jest wtedy otwarta - ustaw glowny index na kolumne, po ktorej musisz sortowac/wybierac.

przyklad:
klasyczny przyklad tabelki z drzewkiem
  1. CREATE TABLE `test` (
  2. `test_id` smallint(4) UNSIGNED NOT NULL AUTO_INCREMENT,
  3. `code` char(10) NOT NULL,
  4. `description` char(200) NOT NULL DEFAULT '',
  5. `lft` int(6) DEFAULT NULL,
  6. `rgt` int(6) DEFAULT NULL,
  7. PRIMARY KEY (`test_id`),
  8. KEY `code` (`code`)
  9. )


zmiana kluczy

  1. CREATE TABLE `test2` (
  2. `test_id` smallint(4) UNSIGNED NOT NULL,
  3. `code` char(10) NOT NULL,
  4. `description` char(200) NOT NULL DEFAULT '',
  5. `lft` int(6) DEFAULT NULL,
  6. `rgt` int(6) DEFAULT NULL,
  7. KEY `lft` (`lft`),
  8. KEY `rgt` (`rgt`),
  9. KEY `code` (`code`)
  10. )




i teraz samo zapytanie:

  1.  
  2. SELECT SQL_NO_CACHE
  3. node.*
  4. FROM (`test` `node`
  5. JOIN `test` `parent`)
  6. WHERE (`node`.`lft` BETWEEN `parent`.`lft`
  7. AND `parent`.`rgt`)
  8. GROUP BY `node`.`code`
  9. ORDER BY `node`.`lft`



tabela wypelniona okolo 10000 rekordow, jak widac - drzewko kodow.

roznica miedzy jedna tabela a druga to : ~~4 sec vs ~~0.8

po zastanowieniu, i tak przerobilem to jeszcze zupelnie inaczej - ale to juz inna bajka wink.gif (zjazd do ~~0.02, ale to juz tabela RO)

chodzi mi o to ze w powyzszym przykladzie, (przerabialem juz istniejaca tabele) kolumna test_id jest nie tylko zbędna, ale i szkodliwa.
unikalnosc mam zapewniona na poziomie pola code, i to jest takze prawdziwy klucz glowny. (mozna dodac uniqa na tym polu). w tym konkretnym przypadku klucz primary na kolumnie test_id - szkodzi.

edit.:
w wersji bez indexu primary - zapytanie powinno wskazywac na polaczenie po kolumnie code czyli :
  1.  
  2. SELECT SQL_NO_CACHE
  3. node.*
  4. FROM (`test` `node`
  5. JOIN `test` `parent` USING (code)
  6. WHERE (`node`.`lft` BETWEEN `parent`.`lft`
  7. AND `parent`.`rgt`)
  8. GROUP BY `node`.`code`
  9. ORDER BY `node`.`lft`


zapytanie wtedy jeszcze znaczniej przyspieszy. [w testach, ale juz na mocniejszej maszynie - do ~~0.1 s]

j.
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.