Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Proste zapytanie prosta optymalizacja :)
little_MASTER
post
Post #1





Grupa: Zarejestrowani
Postów: 29
Pomógł: 0
Dołączył: 8.12.2008

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


Witam wszystkich!
Mam malutki problem odnośnie zapytania.
Otóż mam zapytanko:
  1. SELECT r.reg_nazwa AS region, w.woj_nazwa, o.typ, o.nazwa, o.ulica, o.kod, o.miasto, o.email, o.telefon, o.www, o.opis, o.notatki, o.cenaszkolenia, o.zdjecie FROM osoby o LEFT JOIN regiony r ON o.reg_id=r.reg_id LEFT JOIN wojewodztwa w ON o.woj_id = w.woj_id WHERE wew_id='E000001'

Które wybiera jakieś tam szczegóły z tabeli osoby, a że region jest jako id to wypadałoby pobrać nazwę z innej tabeli, podobnie dla województw.
Ogółem zapytanie działa (IMG:style_emoticons/default/smile.gif)

Ale...
To jedno zapytanie dla jak na razie 100 rekordów w tabeli osoby wykonuje się w 0,0006 sekundy (czyli dość szybko w/g mnie).

Tylko zastanawiam się jak to coś zoptymalizować, tzn same zapytanie, czy muszę robić dwa razy JOIN i czy koniecznie musi być LEFT, może jakiś inny.

A tak na marginesie czy w przypadku takich zapytań ma znaczenie jak ustawiłem indexy?

Dla tabeli osoby mam jako indexy: wew_id jako primary, reg_id i woj_id jako pomocnicze.

W razie potrzeby przedstawię strukturę wizualnie, choć wydaje mi się że jest ona dość zrozumiała (IMG:style_emoticons/default/smile.gif)

Prosiłbym o pomoc, wykładowca dość poważnie ocenia optymalizację zapytań, a chciałbym to jakoś spokojnie zaliczyć (IMG:style_emoticons/default/tongue.gif)

Z góry dzięki za pomoc.

//EDIT
Stworzyłem jeszcze takie zapytanko:
  1. SELECT r.reg_nazwa AS region, w.woj_nazwa, o.typ, o.nazwa, o.ulica, o.kod, o.miasto, o.email, o.telefon, o.www, o.opis, o.notatki, o.cenaszkolenia, o.zdjecie FROM osoby o, regiony r, wojewodztwa w WHERE o.reg_id=r.reg_id AND o.woj_id = w.woj_id AND wew_id='E000001'

I teraz kolejne pytanie: które z tych dwóch jest lepsze? Które w przypadku dużej ilości rekordów będzie lepsze?

Wiem że mogę wygenerować 100tys albo i więcej rekordów i to sprawdzić, ale jednak wolałbym poradzić się doświadczonych koderów najpierw (IMG:style_emoticons/default/wink.gif)

Ten post edytował little_MASTER 25.10.2011, 11:37:00
Go to the top of the page
+Quote Post
nospor
post
Post #2





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




Same joiny masz zrobione poprawnie.
Co do indeksów, to zrób sobie EXPLAIN i zobacz czy mysql z nich korzysta.
Go to the top of the page
+Quote Post
luckyps
post
Post #3





Grupa: Zarejestrowani
Postów: 153
Pomógł: 41
Dołączył: 28.06.2011
Skąd: Częstochowa

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


Cytat(little_MASTER @ 25.10.2011, 12:30:14 ) *
I teraz kolejne pytanie: które z tych dwóch jest lepsze? Które w przypadku dużej ilości rekordów będzie lepsze?

Przy tak skonstruowanych zapytaniach wyniki mogą się różnic (ale to juz zalezy od danych) - poczytaj jak jest roznica pomiedzy LEFT join a INNER join
Go to the top of the page
+Quote Post
little_MASTER
post
Post #4





Grupa: Zarejestrowani
Postów: 29
Pomógł: 0
Dołączył: 8.12.2008

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


(IMG:http://img835.imageshack.us/img835/3771/screen1bx.jpg)
Tak wygląda tabelka z profilowania.
Czy korzysta w index'ów nie wiem :/
Prosiłbym o interpretację (IMG:style_emoticons/default/smile.gif)

Kiedy pierwszy raz wykonałem zapytanie trwało ono 0,0026s, za drugim i kolejnym razem 0,0006s.
Czy to normalne? MySQL sam cache'uje wyniki zapytań?

A i jeszcze jedno (tylko nie krzyczcie za bardzo):
W bazie mam relacje n do n tzn jedna osoba może należeć do wielu typów.
Mam tabele osoba i tabelę typ i pomiędzy nimi była tabela osoba_typ która przechowywała id_osoba i id_typ

Pominąłem to dodając pole typ które przechowuje połączone kropką typy.
Np jeżeli coś było typu 1,2 i 5 to w polu typ mam 1.2.5.
W php robię implode a przy odczycie explode.

Wszystko śmiga aż miło (IMG:style_emoticons/default/smile.gif)

Ale co jeśli będę chciał robić wybieranie po typie? Zostaje mi tylko like, a to przy dużych ilościach rekordów zabija bazę :/ (poprawcie mnie jeśli się mylę)

Czy to co zrobiłem było słuszne? Tzn czy tak się powinno robić? Muszę mieć jakąś linię obrony dla wykładowcy (IMG:style_emoticons/default/smile.gif)

Ten post edytował little_MASTER 25.10.2011, 11:52:51
Go to the top of the page
+Quote Post
nospor
post
Post #5





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




Prosiłem o EXPLAIN a nie ok profilowanie
Go to the top of the page
+Quote Post
little_MASTER
post
Post #6





Grupa: Zarejestrowani
Postów: 29
Pomógł: 0
Dołączył: 8.12.2008

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


Cytat(nospor @ 25.10.2011, 12:48:15 ) *
Prosiłem o EXPLAIN a nie ok profilowanie


Przepraszam (IMG:style_emoticons/default/smile.gif)

Zwykłe nieporozumienie, szybka lektura na goolach i już wiem co to EXPLAIN

Dla zapytania z joinami ma się to tak:
(IMG:http://img638.imageshack.us/img638/4254/screen2ca.jpg)
a dla zapytania tylko z selectem tak:
(IMG:http://img585.imageshack.us/img585/8712/screen3ln.jpg)
Go to the top of the page
+Quote Post
nospor
post
Post #7





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




No i już widzisz które lepsze (IMG:style_emoticons/default/smile.gif) Jedno pobiera 1 rekord, drugie az 36

Spróbuj dla drugiej opcji ten warunek wew_id='E000001' dać jako pierwszy a nie jak teraz masz ostatni
Go to the top of the page
+Quote Post
luckyps
post
Post #8





Grupa: Zarejestrowani
Postów: 153
Pomógł: 41
Dołączył: 28.06.2011
Skąd: Częstochowa

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


Cytat(little_MASTER @ 25.10.2011, 12:42:21 ) *
Pominąłem to dodając pole typ które przechowuje połączone kropką typy.
Np jeżeli coś było typu 1,2 i 5 to w polu typ mam 1.2.5.
W php robię implode a przy odczycie explode.

Wg mnie rozwiazania w takim stylu sa calkowicie do bani (niepraktyczne).

PS: Jak Ty chciales na tej kolumnie robic zapytania z LIKE (IMG:style_emoticons/default/questionmark.gif)
Go to the top of the page
+Quote Post
little_MASTER
post
Post #9





Grupa: Zarejestrowani
Postów: 29
Pomógł: 0
Dołączył: 8.12.2008

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


Cytat(luckyps @ 25.10.2011, 13:04:51 ) *
PS: Jak Ty chciales na tej kolumnie robic zapytania z LIKE (IMG:style_emoticons/default/questionmark.gif)


Tak najszybciej mi przychodzi do głowy:
  1. SELECT * FROM `osoby` WHERE typ LIKE '%.1.%'


Celowo dałem kropkę przed i po 1 bo gdyby jej nie było wybrał by też 10, 11, itd.
Ale nie ukrywam że to chyba nie jest najlepsze rozwiązanie :/

Cytat(nospor @ 25.10.2011, 13:03:10 ) *
Spróbuj dla drugiej opcji ten warunek wew_id='E000001' dać jako pierwszy a nie jak teraz masz ostatni


Jest bez zmian, dalej pobiera 36 rekordów, czyli opcja z JOIN zostaje (IMG:style_emoticons/default/smile.gif)


A czy mając tabelę pośrednią osoba_typ można jednym zapytaniem pobrać szczegóły o osobie i wszystkie jej typy?

Coś na zasadzie:
SELECT o.pole1, o.pole 2, w.nazwa, r.nazwa (a tutaj jakieś zapytanie które pobierze typy dla wskazanego obiektu i połaczy je w string jakoś) FROM osoby o LEFT JOIN wojewodztwa w ON o.wojid=w.id LEFT JOIN region r ON o.regid=r.id WHERE o.id=12;

Czy takie coś jest możliwe? Czy lepiej dwoma zapytaniami, najpierw pobrać info o osobie, a w drugim typy?

Ten post edytował little_MASTER 25.10.2011, 12:44:03
Go to the top of the page
+Quote Post
nospor
post
Post #10





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




Co to za typy, ile ich będziesz miał, czy one są dynamiczne?
Go to the top of the page
+Quote Post
little_MASTER
post
Post #11





Grupa: Zarejestrowani
Postów: 29
Pomógł: 0
Dołączył: 8.12.2008

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


Typy to tak jakby kategorie, tzn osoba może być lekarzem, lektorem, kierowcą itd.
Są one w tabeli typ, ma ona dwie kolumny id i nazwa.

Będą dodawane z poziomu panelu administratora, także jeśli zajdzie potrzeba to będą dodane, ich nazwy zmieniane, w ostateczności usunięte.

Po prostu w panelu chce mieć checkboxy i tam wybierać odpowiednie typy, jeden, kilka, jeśli nie będzie odpowiedniego dla osoby to w osobnym panelu go dodam do tej tablicy (IMG:style_emoticons/default/smile.gif)
A na stronie gdy mam wyświetlane informacje o osobie chciałbym żeby jako lista wyświetliły się typy.

Mam nadzieję że to tak zrozumiale opisałem (IMG:style_emoticons/default/smile.gif)
Go to the top of the page
+Quote Post
nospor
post
Post #12





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




To musisz dodać tabelę łączącą osobę z typem:
osoba_typ
ID_OSOBA
ID_TYP

i wywalic to durne pole typ z osoby (IMG:style_emoticons/default/smile.gif)
Go to the top of the page
+Quote Post
little_MASTER
post
Post #13





Grupa: Zarejestrowani
Postów: 29
Pomógł: 0
Dołączył: 8.12.2008

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


Cytat(nospor @ 25.10.2011, 14:49:15 ) *
To musisz dodać tabelę łączącą osobę z typem:
osoba_typ
ID_OSOBA
ID_TYP

i wywalic to durne pole typ z osoby (IMG:style_emoticons/default/smile.gif)


Też tak myślałem, tzn miałem na początku tak zrobione (IMG:style_emoticons/default/smile.gif)
Całe szczęście jakaś kopia jest to przywrócę.

A da się jednym zapytaniem pobrać i informacje o osobie i typy? Ma to sens?
Czy dwoma zapytaniami?

Czyli jak mamy tabele:

Osoby
-----------------------
|id| imie|nazwisko|
|1|Tomek| xxx|
|2| Karol| xxx|

Typy
----------------
|id| nazwa|
|1| kierowca|
|2|nauczyciel|
...


Osoby_typy
---------------------
|id_osoby|id_typu|
| 1| 1|
| 2| 1|
| 2| 2|

O osobie pobieram tak jak wcześniej tzn po id.
A typy mogę pobrać tak:
  1. SELECT nazwa FROM Typy WHERE id IN (SELECT id_typu FROM Osoby_typy WHERE id_osoby='id_osoby')

Czy jakieś rozwiązanie byłoby lepsze?
Chodzi o to że osób będzie dużo, jak i typów i żeby przy zaliczeniu mi się to nie wysypało albo nie zamuliło :/

Dużo osób to np 100tys danych testowych :/
Go to the top of the page
+Quote Post
piotr.kazmiercza...
post
Post #14





Grupa: Zarejestrowani
Postów: 47
Pomógł: 10
Dołączył: 1.03.2010
Skąd: Warszawa

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


Ogólnie jeżeli zależy Ci na prędkości działania bazy to:

1) Olej postacie normalne w bazie.

  1. SELECT r.reg_nazwa AS region, w.woj_nazwa, o.typ, o.nazwa, o.ulica, o.kod, o.miasto, o.email, o.telefon, o.www, o.opis, o.notatki, o.cenaszkolenia, o.zdjecie FROM osoby o LEFT JOIN regiony r ON o.reg_id=r.reg_id LEFT JOIN wojewodztwa w ON o.woj_id = w.woj_id WHERE wew_id='E000001'


Zmień na

  1. SELECT o.reg_nazwa AS region, o.woj_nazwa, o.typ, o.nazwa, o.ulica, o.kod, o.miasto, o.email, o.telefon, o.www, o.opis, o.notatki, o.cenaszkolenia, o.zdjecie FROM osoby o WHERE wew_id=1


Czyli co zmienić i dlaczego:
Jeżeli robisz dużo zapytań gdzie szukasz np informacji o użytkowniku jak w tym przypadku, wszystko co się da umieść w tej samej tabelce. Z zapytania widzę że potrzebujesz przy wyjmowaniu jedynie nazwę województwa w takim razie nie ma potrzeby JOINa. Jest to bardzo "sztywne" rozwiązanie jednak w znaczącym stopniu przyśpiesza zapytania.

2) Użyj MEMORY

Ogólnie jeżeli przechowujesz w bazie dane które będą rzadko/nigdy nie zmieniały używaj dla nich typu tablic: MEMORY. Tablice te będą przechowywane wyłącznie w pamięci więc dostęp do nich będzie dużooo szybszy. W tym wypadku do MEMORY wstawiasz rediony i wojewodztwa

3) Użyj widoków

Jeżeli spodziewasz się częstego odpytywania o dane. Stwórz z tych zapytań z Joinami gotowe widoki (View). Są to tymczasowe tabele utworzone z zapytań. Przydatne i szybkie rozwiązanie.

Wybór rozwiązania zależy od
- ilości danych (100tys to bardzo mało, zabawa zaczyna się przy 30 mil.)
- częstotliwości dodawania nowych danych (predykcja)
- częstotliwości aktualizacji istniejących danych(predykcja)
Go to the top of the page
+Quote Post
little_MASTER
post
Post #15





Grupa: Zarejestrowani
Postów: 29
Pomógł: 0
Dołączył: 8.12.2008

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


Wszystko jest w tabeli użytkownicy, jedyne co potrzebuję to nazwę regionu z którego pochodzi użytkownik (relacja n-1 wielu użytkowników jeden region, czyli użytkownik może mieć wybrany tylko jeden.) podobnie z województwami.

Regiony będą się zmieniały sporadycznie, tzn czasami dojdą nowe, województwa, hmm to po jakiś zmianach administracyjnych w Polsce (IMG:style_emoticons/default/tongue.gif)

Czyli rozumiem żeby robić SELECTA z bazy osoby normalnie, a co z regionami i województwami? Jakiś przykładzik zastosowania tego MEMORY? (IMG:style_emoticons/default/wink.gif)

Osoby będą dodane raz (przeze mnie na potrzeby zaliczenia:)), ale w założeniu mam, że kilka osób może dodawać/aktualizować dane odnośnie osób z poziomu panelu administracyjnego, chodzi o to żeby mieć poziomy uprawnień, ale to poza tematem troszkę.
Wykładowca wpisał mi że powinna być możliwość dodawania/edycji/usuwania przez kilka osób na raz.

A odnośnie edycji/usuwania: jak zabezpieczyć tabelę osoby przed edycją/usunięciem rekordu w tym samym czasie?
Czyli ktoś edytuje rekord a druga osoba chce go usunąć, albo ktoś edytuje, a druga osoba chce to samo zrobić.
Myślałem o tym żeby zrobić timestamp w momencie kiedy zaczynamy edytować, tzn ktoś wchodzi w edycję jakieś osoby, w rekordzie w polu timestamp ustawia się czas+pół godziny i mamy czas na edycję, po zaptwierdzeniu zmian timestamp robimy null. W razie wyłączenia kompa/przeglądarki po pół godziny edycja będzie dostępna dla kogoś innego.

A co do widoków, to czy dużo pomagają przy częstych zapytaniach? Przykładowo 4 razy na sekundę? Strona i tak jest na smarty więc jest cachowana, ale zastanawiam się czy dodatkowo cachować wyniki z zapytań z bazy?

//BTW
Co z tabelą na silniku MEMORY kiedy zrobimy restart serwera? Tzn w tym przypadku mojego kompa? Czy dane są usuwane? Chyba tak skoro są w pamięci tylko. Ale czy są przywracane po ponownym starcie serwera MySQL?

Ten post edytował little_MASTER 25.10.2011, 19:28:12
Go to the top of the page
+Quote Post
piotr.kazmiercza...
post
Post #16





Grupa: Zarejestrowani
Postów: 47
Pomógł: 10
Dołączył: 1.03.2010
Skąd: Warszawa

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


Jeżeli to na jakieś zaliczenie to pomiń moją propozycje z umieszczeniem nazw regionów i województw w jednej tabeli bo Cie wykładowca na pal nabije (IMG:style_emoticons/default/smile.gif) .

Cytat(little_MASTER) *
Czyli rozumiem żeby robić SELECTA z bazy osoby normalnie, a co z regionami i województwami? Jakiś przykładzik zastosowania tego MEMORY? (IMG:style_emoticons/default/wink.gif)


MEMORY to tylko typ tablicy robisz do tego takie same zapytania jak każde inne.

Cytat(little_MASTER) *
A odnośnie edycji/usuwania: jak zabezpieczyć tabelę osoby przed edycją/usunięciem rekordu w tym samym czasie?
Czyli ktoś edytuje rekord a druga osoba chce go usunąć, albo ktoś edytuje, a druga osoba chce to samo zrobić.
Myślałem o tym żeby zrobić timestamp w momencie kiedy zaczynamy edytować, tzn ktoś wchodzi w edycję jakieś osoby, w rekordzie w polu timestamp ustawia się czas+pół godziny i mamy czas na edycję, po zaptwierdzeniu zmian timestamp robimy null. W razie wyłączenia kompa/przeglądarki po pół godziny edycja będzie dostępna dla kogoś innego.


Poczytaj o SQL Transaction

Cytat(little_MASTER) *
A co do widoków, to czy dużo pomagają przy częstych zapytaniach? Przykładowo 4 razy na sekundę? Strona i tak jest na smarty więc jest cachowana, ale zastanawiam się czy dodatkowo cachować wyniki z zapytań z bazy?


Widoki warto używać w momencie jak musisz robić JOIN na dużych tablicach. O ile to przyśpiesza to ciężko powiedzieć wszystko zależy od struktury danych.

//BTW
Cytat(little_MASTER) *
Co z tabelą na silniku MEMORY kiedy zrobimy restart serwera? Tzn w tym przypadku mojego kompa? Czy dane są usuwane? Chyba tak skoro są w pamięci tylko. Ale czy są przywracane po ponownym starcie serwera MySQL?


Trzeba to samemu oprogramować, że po restarcie SQL'a wstawiasz do tablic MEMORY dane na nowo. Trochę uciążliwe ale bardzo szybkie.
Go to the top of the page
+Quote Post
little_MASTER
post
Post #17





Grupa: Zarejestrowani
Postów: 29
Pomógł: 0
Dołączył: 8.12.2008

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


Cytat(piotr.kazmierczak @ 25.10.2011, 23:32:18 ) *
Poczytaj o SQL Transaction


Co to transakcje to wiem (IMG:style_emoticons/default/smile.gif) (Tak mi się przynajmniej wydaje (IMG:style_emoticons/default/tongue.gif) )
mysqli ma mechanizm do tego. Mam zrobione tak:
  1. //############### INSERTION ###############
  2. mysqli_autocommit($this->link, false);
  3. mysqli_query($this->link, "SET NAMES 'utf8'");
  4. $q = "insert into osoby zapytanie";
  5.  
  6. mysqli_query($this->link, $q);
  7. if (mysqli_errno($this->link))
  8. {
  9. mysqli_rollback($this->link);
  10. return - 1;
  11. } else
  12. {
  13. $newID = mysqli_insert_id($this->link);
  14. $q = "UPDATE obiekty SET wew_id=CONCAT('E',substring('00000',length(osoba_id)),osoba_id) WHERE obiekt_id = $newID";
  15.  
  16. $wynik = mysqli_query($this->link, $q);
  17. if (mysqli_errno($this->link))
  18. {
  19. mysqli_rollback($this->link);
  20. return - 1;
  21. } else
  22. {
  23. mysqli_commit($this->link);
  24. return 1;
  25. }
  26. }
  27. return - 1;


Ale bardziej chodzi mi o edycje osób.
Tzn i tak muszę zrobić selecta żeby wypełnić formularz edycji, a po zmianie czegokolwiek zrobić update. No chyba oczywiste jest (IMG:style_emoticons/default/smile.gif)
Tylko jak to zabezpieczyć żeby dwie osoby nie edytowały w tym samym czasie tej samej osoby.
Sytuacja jest prosta wydaje mi się, żeby jedna osoba nie zastąpiła przypadkiem pracy innej.

Zrobić jakiś timestamp w tabeli dodatkowo?

Co do tablic memory to chyba zostanę narazie na InnoDB, albo nawet zrezygnyje z transakcji i przejdę na MyISAM, bo z tego co wyczytałem przy operacjach typu select jest szybszy, a jak zaznaczyłem głównie będę wyświetlał dane, edycja będzie sporadyczna (IMG:style_emoticons/default/smile.gif)
Go to the top of the page
+Quote Post

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

 



RSS Aktualny czas: 15.09.2025 - 00:38