Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Podkręcanie GROUP_CONCAT
Forum PHP.pl > Forum > Bazy danych > MySQL
TomASS
Witam serdecznie.

Mam w bazie danych realację jeden do wielu. Mam tabelę transporty oraz miejsca. Do jednego transportu może być przypisanych kilka miejsc.

Aby całość trasy (wszystkie miejsca danego transportu) wyciąnąć w zapytaniu daję GROUP CONCAT:

  1. SELECT T.*, (SELECT GROUP_CONCAT(DISTINCT M.Miejscowosc ORDER BY Rodzaj DESC SEPARATOR '-') FROM m_miejsca AS M WHERE M.ID_transport=T.ID_t AND M.Miejsce!='del') FROM m_transporty AS T;


Niestety daje mi to czas 8 sekund dla 100 rekordow. Po usunieciu GROUP_CONCAT daje mi czas 0,03s.
Co próbowałem:
1. Uproszczenie GROUP_CONCAT: (SELECT GROUP_CONCAT(M.Miejscowosc SEPARATOR '-') FROM m_miejsca AS M WHERE M.ID_transport=T.ID_t ) - nic nie daje (minimalne przyspieszenie - 1,5%)
2. Przeniesienie funkcjonalności do "STORED FUNCTION" - także nic


Czy ktoś ma jakiś pomysł?
sazian
daj explain
pewnie wystarczy indeksy poprawić
TomASS
Zapomniałem powiedzieć - indeksy działają. Rozmiar danych nie jest zatrważający:
transporty 14 000 rekordów
miejsca 32 000 rekordów
sazian
a masz pewność że indeksy są prawidłowe ?
dla pewności pokarz wynik explain
TomASS
EXPLAIN pokazuje:
Cytat
id = 2
select_type = DEPENDENT SUBQUERY
table = M
type = ALL
possible_keys = ID_transport,ID_transport_2
key = NULL
key_len = NULL
ref = NULL
rows = 28735
Extra = Using where


Podzapytanie:
  1. (SELECT GROUP_CONCAT(DISTINCT M.Miejscowosc ORDER BY Rodzaj DESC SEPARATOR '-') FROM m_miejsca AS M WHERE M.ID_transport=T.ID_t AND M.Miejsce!='del')


Indeksy dla tabeli m_miejsca:
Pierwszy: ID_transport
Drugi: ID_transport + Miejsce

sad.gif
mortus
Nie ma się co dziwić, że zapytanie działa tak długo, ponieważ sprawdza setki kombinacji, zanim trafi na odpowiedni zbiór pogrupowanych miejscowości. Jest po prostu trochę źle napisane:
  1. SELECT T.*,
  2. (
  3. SELECT GROUP_CONCAT(M.Miejscowosc SEPARATOR '-')
  4. FROM m_miejsca AS M
  5. WHERE M.Miejsce != 'del'
  6. GROUP BY M.ID_transport
  7. HAVING M.ID_Transport = T.ID_t
  8. ) AS Miejscowosci FROM m_transporty AS T;

Nie wiem czemu ma służyć sortowanie, ale dopisać nie będzie trudno.

Grupujemy i łączymy tylko te rekordy, których M.ID_transport jest zgodne z T.ID_t.

PS1: Usunąłem część odpowiedzi, bo brzmiała dwuznacznie, tzn. można ją było dwuznacznie zrozumieć.

PS2: Teraz tak patrzę i widzę, że różnica jest niewielka. Zastanawiam się tylko, po co Ci DISTINCT w podzapytaniu i dlaczego te tabele są połączone relacją jeden do wielu. Wydawać by się mogło, że do tego potrzebne są trzy tabele i relacja wiele do wielu, ale to zależy od założeń aplikacji.
TomASS
Zmieniłem - teraz działa 16 sekund :/

Dokładnie to wygląda teraz tak:

  1. SELECT DISTINCT
  2. T.Numer, T.Numer_zleceniodawcy, Zlecniodawca.Nazwa, T.ID_przewoznik,
  3. Przewoznik.Nazwa, 1, Odleglosc, ROUND(T.Cena_jednostkowa_netto_od_sprzedawcy*T.Ilosc,2),
  4. T.Waluta_zlecenia, ROUND(T.Cena_netto_dla_przewoznika,2), T.Waluta_dla_przewoznika,
  5. T.Towar, CONCAT_WS(' ',Kierowca.Nazwisko, Kierowca.Imie), Kierowca_spedycja,
  6. Ciezarowka.Numer_ciagnika, Ciezarowka_spedycja, Faktura.Numer, DATE(T.Data_realizacjiPoczatek),
  7. DATE(T.Data_realizacjiKoniec), Rodzaj_transportu, T.ID_t, Oddzial.Nazwa, ' ', NumerFakturyOdPrzewoznika, T.FakturaObca,
  8. (SELECT GROUP_CONCAT(M.Miejscowosc ORDER BY Rodzaj DESC SEPARATOR '-')
  9. FROM m_miejsca AS M
  10. WHERE M.Miejsce != 'del' AND M.ID_Transport = T.ID_t
  11. GROUP BY M.ID_transport)
  12. FROM
  13. m_transporty AS T LEFT JOIN
  14. m_kontrahenci AS Zlecniodawca ON (Zlecniodawca.ID_k=T.ID_platnik) LEFT JOIN
  15. m_kontrahenci AS Przewoznik ON (Przewoznik.ID_k=T.ID_przewoznik) LEFT JOIN
  16. m_kierowcy AS Kierowca ON (Kierowca.ID_k=T.ID_kierowca) LEFT JOIN
  17. m_ciezarowki AS Ciezarowka ON (Ciezarowka.ID_c=T.ID_ciezarowka) LEFT JOIN
  18. f_faktury AS Faktura ON (Faktura.ID_f=T.ID_faktura) LEFT JOIN
  19. m_oddzialy AS Oddzial ON (Oddzial.ID_o=T.ID_oddzial)
  20. WHERE
  21. T.Miejsce!='del' AND
  22. 1 AND
  23. WEEK(T.Data_realizacjiPoczatek)='2' AND
  24. YEAR(T.Data_realizacjiPoczatek)='2013'
  25. ORDER BY T.Numer ASC


Przeniosłem "M.ID_Transport = T.ID_t" z HAVING do WHERE i jest lepiej - 4sekundy, ale nadal kiepsko :/
mmmmmmm
spróbuj tak:
  1. SELECT DISTINCT
  2. T.Numer,
  3. T.Numer_zleceniodawcy,
  4. Zlecniodawca.Nazwa,
  5. T.ID_przewoznik,
  6. Przewoznik.Nazwa,
  7. 1,
  8. Odleglosc,
  9. ROUND(T.Cena_jednostkowa_netto_od_sprzedawcy * T.Ilosc,
  10. 2),
  11. T.Waluta_zlecenia,
  12. ROUND(T.Cena_netto_dla_przewoznika, 2),
  13. T.Waluta_dla_przewoznika,
  14. T.Towar,
  15. CONCAT_WS(' ', Kierowca.Nazwisko, Kierowca.Imie),
  16. Kierowca_spedycja,
  17. Ciezarowka.Numer_ciagnika,
  18. Ciezarowka_spedycja,
  19. Faktura.Numer,
  20. DATE(T.Data_realizacjiPoczatek),
  21. DATE(T.Data_realizacjiKoniec),
  22. Rodzaj_transportu,
  23. T.ID_t,
  24. Oddzial.Nazwa,
  25. ' ',
  26. NumerFakturyOdPrzewoznika,
  27. T.FakturaObca,
  28. x.group_miejscowosc
  29. FROM
  30. m_transporty AS T
  31. LEFT JOIN
  32. m_kontrahenci AS Zlecniodawca ON (Zlecniodawca.ID_k = T.ID_platnik)
  33. LEFT JOIN
  34. m_kontrahenci AS Przewoznik ON (Przewoznik.ID_k = T.ID_przewoznik)
  35. LEFT JOIN
  36. m_kierowcy AS Kierowca ON (Kierowca.ID_k = T.ID_kierowca)
  37. LEFT JOIN
  38. m_ciezarowki AS Ciezarowka ON (Ciezarowka.ID_c = T.ID_ciezarowka)
  39. LEFT JOIN
  40. f_faktury AS Faktura ON (Faktura.ID_f = T.ID_faktura)
  41. LEFT JOIN
  42. m_oddzialy AS Oddzial ON (Oddzial.ID_o = T.ID_oddzial)
  43. LEFT JOIN
  44. (SELECT
  45. ID_Transport,
  46. GROUP_CONCAT(M.Miejscowosc
  47. ORDER BY Rodzaj DESC
  48. SEPARATOR '-') group_miejscowosc
  49. FROM
  50. m_miejsca AS M
  51. WHERE
  52. M.Miejsce != 'del'
  53. GROUP BY ID_Transport) x ON (x.ID_Transport = T.ID_t)
  54. WHERE
  55. T.Miejsce != 'del'
  56. AND WEEK(T.Data_realizacjiPoczatek) = 2
  57. AND YEAR(T.Data_realizacjiPoczatek) = 2013
  58. ORDER BY
  59. T.Numer ASC
TomASS
Nic sad.gif Widzę, że wywalenie DISTINCT pomaga (do 0,4s). Jakoś to sobie przemodeluje aby DISTINCT (po pierwszym SELECT) nie występował. Dziękuję wszystkim za pomoc.
mortus
Od razu trzeba było powiedzieć, że takiego kolosa testujesz, bo takie wyniki przy pierwszym podanym przez Ciebie zapytaniu i 100 rekordach w moich oczach były po prostu niemożliwe. Z DISTINCT to jest tak, że w przypadku posortowanego zbioru danych (ORDER BY) tworzona jest tabela tymczasowa i to z niej są wybierane rekordy bez duplikatów... stąd tak długi czas wykonywania zapytania. Z założenia DISTINCT ma służyć do wybrania rekordów bez powtórzeń, ale to samo można osiągnąć grupując powtarzające się rekordy według odpowiednich kolumn. DISTINCT to po prostu "specjalny przypadek GROUP BY". Zastanawia mnie tylko skąd założenie, że przy tak dużej ilości danych (gdzie każde T.ID_t powinno być unikalne) pojawią się duplikaty.
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.