Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: query z kilku tabel - opytmalizacja
Forum PHP.pl > Forum > Bazy danych > MySQL
zombie
Witam! Mam takie zapytanie do MySQL'a:
[sql:1:852d7fa5ee]SELECT tabela.id, tabela.imie, tabela.plec, tabela.ur, (YEAR(CURDATE())-YEAR(tabela.ur)) - (RIGHT(CURDATE(),5)<RIGHT(tabela.ur,5)) AS wiek, tabela.city, tabela.icq, tabela.gg, tabela.tlen, tabela.wpk, tabela.aqq, DATE_FORMAT(tabela.data,'%Y-%m-%d'), tabela.zdjecie, tabela.banned, ((tabela_pkt.humor*3) + tabela_pkt.sciana) AS punkty FROM tabela, tabela_pkt WHERE tabela.imie LIKE '".$_POST['l']."%' ORDER BY tabela.".$_GET['ord']." ".$aod.", tabela.imie ASC LIMIT ".(($_GET['pg'])*$HM).", ".$HM[/sql:1:852d7fa5ee]

... problem polega na tym, że takie zapytanie wykonuje się wręcz do kilkunastu sekund. Jak można zoptymalizaowć taki kod? Zalezy mi na tym, żeby pobierać dane z wielu tabel jednym zapytaniu, bo to upraszcza sortowanie... Czy jest to w ogóle możliwe, czy konieczna jest opcja, tak, jak na forum, np. "pokaż 10 najlepszych" i wtedy pobrać dane z tabela_pkt a w drugin query z tabela..?
FiDO
Przede wszystkim zmien [sql:1:dd3fb74fae]WHERE tabela.imie LIKE '".$_POST['l']."%'[/sql:1:dd3fb74fae]
na
[sql:1:dd3fb74fae]WHERE INSTR(tabela.imie, '".$_POST['l']."') = 1[/sql:1:dd3fb74fae]
Po drugie zaloz indexy na pola uzywane w ORDER BY
zombie
Dzięki FiDO, ale to mojego problemu nie rozwiązuje... a wręcz przeciwenie,... działa jeszcze wolniej sad.gif
DeyV
Zastanwaie mnie w tym zapytaniu jedno. W jaki sposób te tabele są ze sobą 'łączone' Nie ma żadnego warunku 'łaczącego' je ani w WHERE, ani żadnego JOIN
Może to powoduje nieoptymalne działanie, bo znacznie zwiększa ilość wyników?
Dravo
tabele się łączą przez przecinek (Full Join) [ w klauzuli FROM ]
Według mnie jednak powinno w klauzuli WHERE związać sie obie tabele warunkami np. tab1.id = tab2.id, po prostu musza mieć jakieś wspólne pola, np. id.
Nie jestem ekspertem, ale na chlopski rozum, jeśli nie ma warunków łącznia się tabel, to łączą się całe, co zwalania zapytanie. Jeśli natomiast określimy fragmemnty tabel, które mają się łączyć cay proces może się przyspieszyć.
Jednak trzebabyłoby to sprawdzić.

UPDATE 1
Stosuj aliasy do nazwy tabel i pól [w tym przypadku zwłaszcza do tabel], np. tabela_pkt as pkt.
DeyV
tak się zastanawiam, czy ja nie napisałem tego samego, co Ty, Dravo?

ps. aliasy nie mają znaczenia dla prędkości.
zombie
Dzieki Panowie, rzeczywiscie pomoglo [sql:1:21324c5209]WHERE tabela.kolumna = tabela_pkt.kolumna[/sql:1:21324c5209]
FiDO
No to teraz porownaj sobie wydajnosc z tym co podalem i bez tego.
Tylko doloz jeszcze indeksy na pola laczace dwie tabele (te w powyzszym WHERE)
zombie
Spox. Gania jak wariat teraz... smile.gif acz różnica pomiędzy LIKE a INSTR zdaje się być niewielka ...
Indeo
Tu już nawet nie chodzi o optymalizację ale o dosłowne mordowanie bazy danych.

Po pierwsze. Konstrukcja łączenia tabel bez użycia instrukcji INNER JOIN, LEFT JOIN itd. nadaje się do używania przez przedszkolaków. Jak ktoś nie wierzy to niech sobie zrobi najprostszą kwerendę w MSAccess i zobaczy, że nawet głupi Acces od razu tworzy instrukcji INNER.

Łączenie tabel winno się odbywac następująco:
[sql:1:d05366fede]
SELECT tabela1.pole2,tabela2.pole2 FROM tabela1 INNER JOIN tabela2 ON tabela1.pole1=tabela2.pole1 where tabela1.pole4 LIKE '%tra la la%'
[/sql:1:d05366fede]
Po drugie indeksy, indeksy i jeszcze raz indeksy. Na kazdym kroku gdzie łączy się tabele należy używac indeksów jako kluczy łączących tabele. W przeciwnym razie zapytanie do bazy może być tak bolesne że obciąży procesor w 100% i zawiesi komputer.

Jeśli ktoś sobie nie radzi lub wręcz zapisanie tego co sie chce zrobić w jednym zapytaniu jest niemożliwe to nikomu korona z głowy nie spadnie jak rozłoży zapytanie na kilka podzapytań, których wyniki załaduje do nowych tabel (temporary table), uworzy w nich indeksy i wykona ostateczne zapytanie.
Przestudiowanie 3 prostych zapytań jest 100 razy szybsze niż ślęczenie 6 godzin nad zapytaniem w którym użyto 4 instrukcje IF jedna w drugiej.

A oto przykładowe zapytanie do bazy na której pracuję i niech ktoś spróbuje zapisać takie zapytanie bez INNERÓW smile.gif:

[sql:1:d05366fede]
#zrzuty na pktach zlewnych
drop table polacz15;
create temporary table polacz15 select distinct nr_polacz from polaczenia where polaczenia.adres like '%cieki%zlewny%' or polaczenia.adres like '%cieki%wykaz%';

alter table polacz15 add primary key(nr_polacz);

#Zużycia na punkcie zlewnym
select count(distinct symb_polacz),rach.taryfa,platnik.symb_plat,platnik.nazwa,polaczenia.symb_polacz,
olaczenia.adres,
sum(if(rach.taryfa like '_R' or rach.taryfa like '_W',rach.zuzycie-ifnull(rach_1.zuzycie,0),0)) as woda,
sum(if(rach.taryfa like '_R' or rach.taryfa like '_S',rach.zuzycie-ifnull(rach_1.zuzycie,0),0)) as scieki
from polacz15
left join rach on polacz15.nr_polacz=rach.nr_polacz
left join rach as rach_1 on rach.rach_rekl_nr=rach_1.nr_rach and rach.nr_plat=rach_1.nr_plat and rach.nr_polacz=rach_1.nr_polacz and
rach.nr_lewego=rach_1.nr_lewego and rach.taryfa=rach_1.taryfa and rach.cena_wody=rach_1.cena_wody and rach.cena_sciekow=rach_1.cena_sciekow
inner join polaczenia on rach.nr_polacz=polaczenia.nr_polacz
inner join platnik on rach.nr_plat=platnik.nr_plat
where
rach.rok_spr=2004 and rach.mies_spr=1
group by taryfa

order by symb_plat

[/sql:1:d05366fede]

Pozdrawiam
FiDO
A mozesz podac konkrety? I jeszcze porownanie z i bez indexow?

Nie mialem do tej pory mozliwosci sprawdzenia tego na wiekszej ilosci danych, a chetnie bym sie dowiedzial na ile teoria przeklada sie na praktyke smile.gif
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.