Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> Optymalizacja zapytania, Mam sporo informacji ale nie potrafię sobie z tym poradzić
TomASS
post 6.07.2011, 13:55:26
Post #1





Grupa: Zarejestrowani
Postów: 1 660
Pomógł: 13
Dołączył: 9.06.2004
Skąd: Wrocław i okolice

Ostrzeżenie: (0%)
-----


Cześć, mam takie oto zapytanie:

  1. SELECT
  2. DISTINCT T.NumerRef
  3. FROM
  4. m_transporty_miejsca AS TM LEFT JOIN
  5. m_transporty AS T ON (T.ID=TM.ID_transport)
  6. WHERE
  7. 1 AND
  8. (AnulowanyFE2='0' OR DateRealizacji>='2011-06-29') AND
  9. 1 AND
  10. T.Odleglosc>='0' AND
  11. T.Odleglosc<='99' AND
  12. T.ImportFE='1' AND
  13. ( T.ID_przewoznik='72' OR (STATUS='przedstawiony' AND 1) OR (Gielda='1' AND STATUS='gielda' AND 1) ) AND
  14. ( Miejsce='akt' OR (STATUS='zaksięgowane w SAP' AND DataDostarczenia IS NULL) )
  15. ORDER BY T.ID


Tabela m_transporty_miejsca 200 000 rekordów
Tabela m_transporty 100 000 rekordów
raczej małe ilości.

Zapytanie wykonuje się około 0,8s (średnia z 20pomiarów).
Jeśli usunę ostatni warunek w klauzuli WHERE:
  1. ( Miejsce='akt' OR (STATUS='zaksięgowane w SAP' AND DataDostarczenia IS NULL) )

a szczególnie
  1. OR (STATUS='zaksięgowane w SAP' AND DataDostarczenia IS NULL)

wówczas czas zapytania spada do 0,03 (średnia z 20pomiarów)

Indexy jakie min. mam pozakładane na tabelę m_transporty to:
ImportFE1: (ImportFE)
ImportFE2: (ImportFE+Miejsce)
ImportFE3: (ImportFE+Status+DataDostarczenia)

EXPLAIN tego zapytania wskazuje, że użyty jest indeks "ImportFE1"

Proszę o pomoc w optymalizacji.


--------------------
Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi (1 - 13)
uupah5
post 6.07.2011, 15:09:10
Post #2





Grupa: Zarejestrowani
Postów: 207
Pomógł: 18
Dołączył: 4.09.2010
Skąd: warszawa

Ostrzeżenie: (0%)
-----


przydałoby się dodać do wszystkich pól aliasy tabel, skoro już dajesz to do publicznej analizy.
pole status występuje kilkukrotnie z czego ostatni raz jest wzajemnie wykluczający się z przedostatnim, więc może jednak przekonstruować to zapytanie tak, aby pole status występowało samodzielnie i nadrzędnie, czyli coś w stylu:
status = a and (....)
or
status = b and (...)
oczywiście to szkic, bo to Ty te dane rozumiesz, więc przebudowanie warunków zostawiam Tobie.
jeśli jest to wykonalne, to dać potem index na status. btw to musi być varchar? oraz rozumiem struktura jest świadomie zdenormalizowana?

a na marginesie, 0.8s to jest faktycznie problem?
Go to the top of the page
+Quote Post
rollen
post 6.07.2011, 20:14:40
Post #3





Grupa: Zarejestrowani
Postów: 16
Pomógł: 6
Dołączył: 24.06.2011

Ostrzeżenie: (0%)
-----


TomASS ma rację spróbuj jakoś przebudować tę część ze STATUS, bo jest całkowicie nieczytelana...

Wskazałeś też, że użyty został indeks ImportFE1: (ImportFE). Nie wiem jakie dane masz w columnie ImportFE, ale wydaje mi się, że to jakiś status z ograniczony do kilku wartości. Taki indeks staje ję mało selektywny. Proponuję go rozszerzyć o T.Odległość.
Czyli ImportFE1: (ImportFE, Odleglosc).
Inne zapytania kożystające z tego indeksu nie ucierpią, a to zapytanie dostanie dodatkowego kopa (o ile są wartości w kolumnie Odleglosc wyższe niż w podanym warunku zakresu[0, 99] )

Cytat(uupah5 @ 6.07.2011, 16:09:10 ) *
a na marginesie, 0.8s to jest faktycznie problem?


Przy tak małej ilości danych w bazie to jest wieczność...
Go to the top of the page
+Quote Post
TomASS
post 6.07.2011, 23:36:23
Post #4





Grupa: Zarejestrowani
Postów: 1 660
Pomógł: 13
Dołączył: 9.06.2004
Skąd: Wrocław i okolice

Ostrzeżenie: (0%)
-----




Cytat(uupah5 @ 6.07.2011, 16:09:10 ) *
pole status występuje kilkukrotnie z czego ostatni raz jest wzajemnie wykluczający się z przedostatnim

Nie wyklucza się - w obydwu przypadkach jest pole status + OR

Cytat(uupah5 @ 6.07.2011, 16:09:10 ) *
więc może jednak przekonstruować to zapytanie tak, aby pole status występowało samodzielnie i nadrzędnie, czyli coś w stylu:
status = a and (....)
or
status = b and (...)

Teraz zrobiłem tak:
  1. SELECT DISTINCT T.NumerRef, T.DataWyjazdu, T.ID_grupa_transportowa ,
  2. T.Pojazd, T.Datetime_do_kiedy_cena_tradycyjna, T.AnulowanyFE2,
  3. T.HD, T.IloscTowaruRzeczywista, T.Multistop, T.DataDostarczenia,
  4. PonaglenieDostarczenia, PonaglenieAwizacji,
  5. TIMESTAMPDIFF(MINUTE, NOW(), T.DateRealizacji) AS CzasDoKoncaRealizacji,
  6. T.PotwierdzeniePrzyjeciaZmian, T.IloscTowaru, T.Odleglosc, T.TerminWaznosci,
  7. T.GodzinaRealizacji, (DataDodaniaNaGielde + INTERVAL TerminWaznosci MINUTE) AS LicytacjaDo,
  8. T.Gielda, T.ID, T.DateRealizacji, T.STATUS, T.Numer, RodzajCiezarowki, ROUND(Cena,2) AS Cena,
  9. Waluta
  10. FROM
  11. m_transporty_miejsca AS TM LEFT JOIN
  12. m_transporty AS T ON (T.ID=TM.ID_transport)
  13. WHERE
  14. 1 AND
  15. (T.AnulowanyFE2='0' OR T.DateRealizacji>='2011-06-30') AND
  16. 1 AND
  17. T.Odleglosc>='9' AND
  18. T.Odleglosc<='102' AND
  19. T.ImportFE='1' AND
  20. ((T.Miejsce='akt' AND ((T.STATUS='przedstawiony' AND 1)
  21. OR (T.STATUS='gielda' AND T.Gielda='1' AND 1)
  22. OR T.ID_przewoznik='72'))
  23. OR (T.STATUS='zaksięgowane w SAP' AND T.DataDostarczenia IS NULL AND T.ID_przewoznik='72'))
  24. ORDER BY T.ID


i dalej nic.
Ze statusem to kłopot taki, że chcę wyswietlać rekordy w różnych statusach, ale przy każdym statusie muszą być spełnione dodakowe (za każdym statusem różne) warunki. Wystarczy, że zajdzie któryś z tych warunków:

1. T.Status='gielda' AND T.Gielda='1' AND 1 AND T.Miejsce='akt
2. T.Status='przedstawiony' AND 1 AND T.Miejsce='akt
3. T.ID_przewoznik='72 AND T.Miejsce='akt (bez różnicy na status)
4. T.Status='zaksięgowane w SAP' AND T.DataDostarczenia IS NULL AND T.ID_przewoznik='72'



Cytat(uupah5 @ 6.07.2011, 16:09:10 ) *
jeśli jest to wykonalne, to dać potem index na status. btw to musi być varchar?

Status ma index - tak jest varchar
Cytat(uupah5 @ 6.07.2011, 16:09:10 ) *
oraz rozumiem struktura jest świadomie zdenormalizowana?

Przypadek sad.gif
Cytat(uupah5 @ 6.07.2011, 16:09:10 ) *
a na marginesie, 0.8s to jest faktycznie problem?

Bardzo długo sad.gif 12razy dłużej niż bym chciał



Cytat(rollen @ 6.07.2011, 21:14:40 ) *
TomASS ma rację spróbuj jakoś przebudować tę część ze STATUS, bo jest całkowicie nieczytelana...

tylko jak ? Moja wyobraźnia chyba tego nie ogarnia już:/
Cytat(rollen @ 6.07.2011, 21:14:40 ) *
Wskazałeś też, że użyty został indeks ImportFE1: (ImportFE). Nie wiem jakie dane masz w columnie ImportFE, ale wydaje mi się, że to jakiś status z ograniczony do kilku wartości.

dwie możliwości - 0 oraz 1
Cytat(rollen @ 6.07.2011, 21:14:40 ) *
Taki indeks staje ję mało selektywny. Proponuję go rozszerzyć o T.Odległość.

pole odegłość to jedna z 20 możliwośći

dzięki chłopaki - pomóżcie jeszcze troszkę, może coś się uda.

Ten post edytował TomASS 7.07.2011, 00:00:15


--------------------
Go to the top of the page
+Quote Post
uupah5
post 7.07.2011, 09:23:55
Post #5





Grupa: Zarejestrowani
Postów: 207
Pomógł: 18
Dołączył: 4.09.2010
Skąd: warszawa

Ostrzeżenie: (0%)
-----


patrząc tylko w kod, bez danych i na sucho. sprawdź sobie czy nie pominąłem jakiegoś warunku.

  1. (...)
  2. T.ImportFE='1' AND
  3. (
  4. (T.STATUS='zaksięgowane w SAP' AND (T.DataDostarczenia IS NULL AND T.ID_przewoznik='72'))
  5. OR
  6. (T.STATUS='przedstawiony' AND (T.Miejsce='akt'))
  7. OR
  8. (T.STATUS='gielda' AND (T.Miejsce='akt' AND T.Gielda='1'))
  9. OR
  10. (T.Miejsce='akt' AND T.ID_przewoznik='72')
  11. )


do tego Status jednak bym znormalizował albo przynajmniej zamienił na small int i oczywiśce zaindeksował.
ta sama uwaga do Miejsce - to też jakiś słownik stanów, zdenormalizowany i na varchar?
Gielda to pole binarne czy też varchar? znasz tylko jeden typ danych?wink.gif

mam nadzieję, że moja propozycja da jakiś efekt. bez danych i struktury tabel opieram się tylko na algebrze boola;)

ps pytałem się czy czas jest krytyczny, bo nie zawsze jest. przykładowo, mógł być to raport co się generuje w nocy.
Go to the top of the page
+Quote Post
TomASS
post 7.07.2011, 10:43:18
Post #6





Grupa: Zarejestrowani
Postów: 1 660
Pomógł: 13
Dołączył: 9.06.2004
Skąd: Wrocław i okolice

Ostrzeżenie: (0%)
-----


Dziękuję za pomoc - mam teraz zapytanie tak:
  1. SELECT DISTINCT T.NumerRef
  2. FROM
  3. m_transporty_miejsca AS TM LEFT JOIN
  4. m_transporty AS T ON (T.ID=TM.ID_transport)
  5. WHERE
  6. 1 AND
  7. (AnulowanyFE2='0' OR DateRealizacji>='2011-06-30') AND
  8. 1 AND
  9. T.Odleglosc>='0' AND
  10. T.Odleglosc<='99' AND
  11. T.ImportFE='1' AND
  12. (
  13. (T.STATUS='zaksięgowane w SAP' AND (T.DataDostarczenia IS NULL AND T.ID_przewoznik='72'))
  14. OR
  15. (T.STATUS='przedstawiony' AND (T.Miejsce='akt'))
  16. OR
  17. (T.STATUS='gielda' AND (T.Miejsce='akt' AND T.Gielda='1'))
  18. OR
  19. (T.Miejsce='akt' AND T.ID_przewoznik='72')
  20. )
  21. ORDER BY T.ID

I nie zanotowałem, żadnego przyspieszenia sad.gif
EXPLAIN wskazuje, że używany nadal jest tylko indeks ImportFE1: (ImportFE).
Pole status ma około 30 wartości a jego zmiana będzie b. kłopotliwa - wolałbym tego uniknąć.

Cytat
Gielda to pole binarne czy też varchar? znasz tylko jeden typ danych?

Gielda - ENUM 0,1,2,3


Ten post edytował TomASS 7.07.2011, 10:44:45


--------------------
Go to the top of the page
+Quote Post
nospor
post 7.07.2011, 10:49:03
Post #7





Grupa: Moderatorzy
Postów: 36 557
Pomógł: 6315
Dołączył: 27.12.2004




STATUS powinno być ENUM a nie smallint. Wówczas nie trzeba będzie zmieniać zapytań, które zapewne się przewalają przez całą aplikację.

Cytat
Gielda - ENUM 0,1,2,3
Trochę dziwne używać ENUM dla wartosci numerycznych wink.gif


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
uupah5
post 7.07.2011, 10:55:19
Post #8





Grupa: Zarejestrowani
Postów: 207
Pomógł: 18
Dołączył: 4.09.2010
Skąd: warszawa

Ostrzeżenie: (0%)
-----


cytat z mauala mysql:
we strongly recommend that you do not use numbers as enumeration values

poza tym, wrzuć w goole "why enum is evil" albo poszukaj na forum, bo chyba już kiedyś zapodawałem linka z art. na ten temat

a co do braku efektu po zmianach - status jest zaindeksowane?

ps piszesz, że chcesz uniknąć przeróbek. obym był złym prorokiem, ale moim zdaniem źle zaprojektowana baza danych da Ci się we znaki jeszcze nie raz

Go to the top of the page
+Quote Post
TomASS
post 7.07.2011, 10:55:32
Post #9





Grupa: Zarejestrowani
Postów: 1 660
Pomógł: 13
Dołączył: 9.06.2004
Skąd: Wrocław i okolice

Ostrzeżenie: (0%)
-----


Cytat(nospor @ 7.07.2011, 11:49:03 ) *
STATUS powinno być ENUM a nie smallint. Wówczas nie trzeba będzie zmieniać zapytań, które zapewne się przewalają przez całą aplikację.

Trochę dziwne używać ENUM dla wartosci numerycznych wink.gif

Sugerujesz, że lepiej zmienić na smallint?


--------------------
Go to the top of the page
+Quote Post
nospor
post 7.07.2011, 10:56:52
Post #10





Grupa: Moderatorzy
Postów: 36 557
Pomógł: 6315
Dołączył: 27.12.2004




Gielda? Mówię jedynie że to jest dziwne wink.gif To tak jakbym używał większej szklanki do trzymania w niej mniejszej szklanki smile.gif


--------------------

"Myśl, myśl, myśl..." - Kubuś Puchatek || "Manual, manual, manual..." - Kubuś Programista
"Szukaj, szukaj, szukaj..." - Kubuś Odkrywca || "Debuguj, debuguj, debuguj..." - Kubuś Developer

Go to the top of the page
+Quote Post
TomASS
post 8.07.2011, 18:34:25
Post #11





Grupa: Zarejestrowani
Postów: 1 660
Pomógł: 13
Dołączył: 9.06.2004
Skąd: Wrocław i okolice

Ostrzeżenie: (0%)
-----


Nic nie dała zmiana pola Status na ENUM sad.gif


--------------------
Go to the top of the page
+Quote Post
ano
post 8.07.2011, 20:38:16
Post #12





Grupa: Zarejestrowani
Postów: 435
Pomógł: 40
Dołączył: 16.02.2003
Skąd: Wrocław

Ostrzeżenie: (0%)
-----


Myślę, że zmiana STATUS i miejsce na pola TINYINT (unsigned przechowuje liczby do 255 więc w zupełności powinno wystarczyć do tych celów) rozwiąże Twój problem. O wiele cięższe jest porównanie stringów niż intów i z tym każdy musi się zgodzić.
W ogóle, wydaje mi się, że to, że status jest przechowywany w formie stringa świadczy o tym, że ktoś źle zaprojektował tą bazę. (jeśli dobrze sobie to wyobrażam, to STATUS to nie jest dowolny string, a jeden spośród listy możliwych statusów - więc idealnie int się do tego nadaje.

Co do pola miejsce to zależy co tam przechowujesz, jak działa na tej samej zasadzie jak "status" to też można by było przerobić na inta.

To pociąga ze sobą oczywiście zmiany w kodzie Twojej aplikacji. Jak wcześniej po prostu wyświetlałeś status to tutaj będziesz musiał robić coś w stylu $status[ID_STATUSU_POBRANE_Z_DB]. i $status = array('blablabla','blabla');
ale myślę, że o takich 'oczywistościach' już nie musze wspominać? wink.gif


--------------------
Linkedin | ...
Go to the top of the page
+Quote Post
everth
post 9.07.2011, 00:04:54
Post #13





Grupa: Zarejestrowani
Postów: 782
Pomógł: 153
Dołączył: 21.07.2010

Ostrzeżenie: (0%)
-----


Abstrahując od powyższych wypowiedzi - zwróciłeś uwagę na fakt że warunek który drastycznie zmienia czas wykonania różni się czymś od pozostałych? Bo jak dla mnie wyróżnia go jedno - klauzula IS(NOT) NULL. Wcześniej wykonujesz LEFT JOIN - złączenie zewnętrzne. Te złączenia nie dają się tak łatwo optymalizować jak złączenia wewnętrzne (INNER JOIN) zwłaszcza że tutaj dokonujesz porównania na wartości NULL (która jest tworzona przez LEFT JOIN). Podejrzewam że z tym warunkiem baza musi przemielić znacznie więcej rekordów niż bez niego - i to pewnie wpływa na czas wykonania. Poeksperymentuj jak to wygląda bez porównań do NULLa (lub spróbuj je jakoś wyeliminować).


--------------------
Już mi się ani wiedzieć, ani tym bardziej myśleć nie chce.
[Think different]!
Go to the top of the page
+Quote Post
TomASS
post 10.07.2011, 13:27:47
Post #14





Grupa: Zarejestrowani
Postów: 1 660
Pomógł: 13
Dołączył: 9.06.2004
Skąd: Wrocław i okolice

Ostrzeżenie: (0%)
-----


Cytat
Abstrahując od powyższych wypowiedzi - zwróciłeś uwagę na fakt że warunek który drastycznie zmienia czas wykonania różni się czymś od pozostałych? Bo jak dla mnie wyróżnia go jedno - klauzula IS(NOT) NULL. Wcześniej wykonujesz LEFT JOIN - złączenie zewnętrzne. Te złączenia nie dają się tak łatwo optymalizować jak złączenia wewnętrzne (INNER JOIN) zwłaszcza że tutaj dokonujesz porównania na wartości NULL (która jest tworzona przez LEFT JOIN). Podejrzewam że z tym warunkiem baza musi przemielić znacznie więcej rekordów niż bez niego - i to pewnie wpływa na czas wykonania. Poeksperymentuj jak to wygląda bez porównań do NULLa (lub spróbuj je jakoś wyeliminować).

Dzięki, całkowite usunięcie warunki z IS NULL nic nie dało w przyspieszeniu zapytania sad.gif


Cytat
rochę dziwne używać ENUM dla wartosci numerycznych

Czy dla pól, które mogą przyjąć tylko wartości 0/1/2/3 - lepiej jest użyć (small)int czy Enum (0/1/2/3)?
Czy konwersja ENUM->SMALLINT nie sprawi mi kłopotu (utrata danych/kłopoty w zapytaniach?)

dzięki.

Ten post edytował TomASS 10.07.2011, 13:35:53


--------------------
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 19.07.2025 - 20:01