Optymalizacja zapytania
Piotr [PKi] - 20-03-2006 01:30
Optymalizacja zapytania
No i jeszcze raz ja, temat ciut inny.
Tabela 'pomiar', wrzucane sa tutaj cyklicznie pomiary w roznych miernikow. obecnie baza ma 18 milionów rekordów. Komp to sempron >2GHz, 1GB ramu.
Struktura tabeli: CREATE TABLE `pomiary` ( `Id` bigint(8) unsigned NOT NULL auto_increment, `data` datetime NOT NULL default '0000-00-00 00:00:00', `miernik` tinyint(3) unsigned NOT NULL default '0', `komorka` smallint(5) unsigned NOT NULL default '0', `wartosc` int(10) NOT NULL default '0', PRIMARY KEY (`Id`), KEY `data` (`data`), KEY `miernik` (`miernik`), KEY `komorka` (`komorka`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci COMMENT='testowa tabela pomiary';
zapytanie: SELECT data, UNIX_TIMESTAMP(data) AS xlabel, (SELECT wartosc FROM pomiary WHERE miernik = 1 AND komorka = 98 AND p.data = data) AS moc_g1, (SELECT wartosc FROM pomiary WHERE miernik = 2 AND komorka = 98 AND p.data = data) AS moc_g2 FROM pomiary AS p WHERE (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(data)) < (60*60*3) GROUP BY data HAVING moc_g1 IS NOT NULL AND moc_g2 IS NOT NULL ORDER BY data ASC;
Zapytanie to wyciaga z bazy wartość dla komorka 98 dla miernik 1 i 2 jako moc_g1 i moc_g2 z ostatnich 3 godzin (60 sekund * 60 minut * 3 godziny).
Obecnie zapytanie trwa 52 sekundy! i daje 334 rekordy, a powinno być 1080 rekordów (rejestracja jest co 10 sekund) i nie wiem czy mysql konczy zapytanie ze wzgedu na jakis timeout czy przepełnienie ??
W podobnej tabeli gdzie pomiar idzie co 5 minut dziala poprawnie, zwraca 36 wierszy z 3 godzin, zapytanie trwa jednak około 6 sekund, rekordów jest około miliona.
Pozdrowionka
Piotr
pedroz - 21-03-2006 01:00
Piotr [PKi] wrote: > zapytanie: > SELECT data, UNIX_TIMESTAMP(data) AS xlabel, > (SELECT wartosc FROM pomiary WHERE miernik = 1 AND komorka = 98 AND > p.data = data) AS moc_g1, > (SELECT wartosc FROM pomiary WHERE miernik = 2 AND komorka = 98 AND > p.data = data) AS moc_g2 > FROM pomiary AS p > WHERE (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(data)) < (60*60*3) > GROUP BY data > HAVING > moc_g1 IS NOT NULL AND > moc_g2 IS NOT NULL > ORDER BY data ASC;
Zrezygnuj z podzapytan. I wydaje mi sie, ze to zapytanie mozna napisac znacznie prosciej. Czy to nie da tych samych wynikow?
SELECT moc_g1.data, UNIX_TIMESTAMP(moc_g1.data) AS xlabel, moc_g1.wartosc, moc_g2.wartosc FROM pomiary as moc_g1, pomiary as moc_g2 WHERE (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(moc_g1.data)) < (60*60*3) and ( moc_g1.miernik = 1 AND moc_g1.komorka = 98 ) and ( moc_g2.miernik = 2 AND moc_g2.komorka = 98 ) and ( moc_g1.data = moc_g2.data ) ORDER BY data ASC;
-- pedroz
Piotr [PKi] - 21-03-2006 01:00
> SELECT > moc_g1.data, UNIX_TIMESTAMP(moc_g1.data) AS xlabel, > moc_g1.wartosc, > moc_g2.wartosc > FROM > pomiary as moc_g1, > pomiary as moc_g2 > WHERE > (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(moc_g1.data)) < (60*60*3) > and ( moc_g1.miernik = 1 AND moc_g1.komorka = 98 ) > and ( moc_g2.miernik = 2 AND moc_g2.komorka = 98 ) > and ( moc_g1.data = moc_g2.data ) > ORDER BY data ASC; >
Nono, zeszło z >50 sekund na 14 sekund.
Mógłbyś wyjasnic dlaczego w FROM tabela pomiary wystepuje 2 razy?
Zmieniłem sobie tak: SELECT moc_g1.data, UNIX_TIMESTAMP(moc_g1.data) AS xlabel, moc_g1.wartosc AS moc_g1, moc_g2.wartosc AS moc_g2 FROM pomiary as moc_g1, pomiary as moc_g2 WHERE (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(moc_g1.data)) < (60*60*3) and ( moc_g1.miernik = 1 AND moc_g1.komorka = 98 ) and ( moc_g2.miernik = 2 AND moc_g2.komorka = 98 ) and ( moc_g1.data = moc_g2.data ) ORDER BY data ASC
zeby wyniki były takie same. Ma to jakieś znaczenie że alias moc_g1 i moc_g2 wystepuje dwa razy?
pedroz - 22-03-2006 00:05
Piotr [PKi] wrote: > > SELECT > > moc_g1.data, UNIX_TIMESTAMP(moc_g1.data) AS xlabel, > > moc_g1.wartosc, > > moc_g2.wartosc > > FROM > > pomiary as moc_g1, > > pomiary as moc_g2 > > WHERE > > (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(moc_g1.data)) < (60*60*3) > > and ( moc_g1.miernik = 1 AND moc_g1.komorka = 98 ) > > and ( moc_g2.miernik = 2 AND moc_g2.komorka = 98 ) > > and ( moc_g1.data = moc_g2.data ) > > ORDER BY data ASC; > > > > > Nono, zeszło z >50 sekund na 14 sekund. > > Mógłbyś wyjasnic dlaczego w FROM tabela pomiary wystepuje 2 razy?
Dlatego, ze z tego co napisales wynika, iz wyniki 2 pomiarow zapisujesz w tej samej kolumnie w 2 rekordach - zgadza sie? W takim wypadku musisz ta tabele dwukrotnie wyciagnac pod innymi aliasami. Zauwaz, ze dzieki temu znika grupowanie po dacie (zamiast tego jest polaczenie tabel po tej wartosci), ktore tez jest czasochlonne.
> Zmieniłem sobie tak: > SELECT > moc_g1.data, UNIX_TIMESTAMP(moc_g1.data) AS xlabel, > moc_g1.wartosc AS moc_g1, > moc_g2.wartosc AS moc_g2 > FROM > pomiary as moc_g1, > pomiary as moc_g2 > WHERE > (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(moc_g1.data)) < (60*60*3) > and ( moc_g1.miernik = 1 AND moc_g1.komorka = 98 ) > and ( moc_g2.miernik = 2 AND moc_g2.komorka = 98 ) > and ( moc_g1.data = moc_g2.data ) > ORDER BY data ASC > > zeby wyniki były takie same. Ma to jakieś znaczenie że alias moc_g1i > moc_g2 wystepuje dwa razy?
Chodzi Ci o te aliasy przy kolumnach? Szczerze mowiac nie mam pojecia jak to dziala w MySQL, ale np. ASE na cos takiego pozwala. Mysle, ze jesli dobrze ulozysz indeks do tego zapytania, to bedziesz mogl zejsc jeszcze nizej ;-) I tworz raczej indeksy wielokolumnowe.
-- pedroz
Piotr [PKi] - 22-03-2006 00:06
> Chodzi Ci o te aliasy przy kolumnach? Szczerze mowiac nie mam pojecia > jak to dziala w MySQL, ale np. ASE na cos takiego pozwala. Mysle, ze > jesli dobrze ulozysz indeks do tego zapytania, to bedziesz mogl zejsc > jeszcze nizej ;-) I tworz raczej indeksy wielokolumnowe. >
Mam indeksy na każdej kolumnie, ale zakładałem pojedyńczo. Czyli wywalić i zrobić
ALTER TABLE pomiary ADD INDEX(id,data,miernik,komorka,wartosc); ??????
pedroz - 22-03-2006 13:13
Piotr [PKi] wrote: > > Chodzi Ci o te aliasy przy kolumnach? Szczerze mowiac nie mam pojecia > > jak to dziala w MySQL, ale np. ASE na cos takiego pozwala. Mysle, ze > > jesli dobrze ulozysz indeks do tego zapytania, to bedziesz mogl zejsc > > jeszcze nizej ;-) I tworz raczej indeksy wielokolumnowe. > > > > Mam indeksy na każdej kolumnie, ale zakładałem pojedyńczo. Czyli wywalić > i zrobić > > ALTER TABLE pomiary ADD INDEX(id,data,miernik,komorka,wartosc); ??????
Wywalic i stworzyc jeden/kilka indeksow, ktore beda zbudowane z kolumn, ktore sa uzywane w klauzuli WHERE, ORDER, GROUP BY. Koumny id i wartosc nie sa wykorzystywane w zadnej z nich, wiec w indeksie nie beda potrzebne. Sproboj na poczatek stworzyc indeks:
ALTER TABLE pomiary ADD INDEX(data,miernik,komorka);
i sprawdzic jak zmieni sie query plan i czasy wykonania zapytania. Jesli nie bedzie satysfakcjonujacy, to mozesz sprobowac przestawic kolejnosc kolumn w indeksie. Niestety nie jestem w stanie Ci pomoc do konca, bo nie znam specyfiki MySQL'a i nie wiem, co na tej bazie moze miec jeszcze wplyw na szybkosc wykonania zapytania. Mozliwe, ze np. wyeliminowanie w tym wyrazeniu
(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(moc_g1.data)) < (60*60*3)
mnozenia i podstawienie juz wyliczonej wartosci moze przyniesc jakies efekty. Mozliwe tez ze lepiej uzyc tu BETWEEN. Wszystko zalezy jak daleko chcesz sie posunac w optymalizacji i jakie czasy sa dla Ciebie akceptowalne. Czasami lepiej zdenormalizowac troche baze i zyskac na wydajnosci. Tu moglbys np. UNIX_TIMESTAMP(moc_g1.data) trzymac zapisane od razu w bazie, co wyeliminowaloby koniecznosc kazdorazowego wyliczania tego wyrazenia przy raporcie. Drog jest wiele ;-)
zanotowane.pldoc.pisz.plpdf.pisz.pleffulla.pev.pl
|
Oracle, SQL, PL/SQL. Jak =?ISO-8859-2?Q?napisa=E6_zapytanie=2C?==?ISO-8859-2?Q?_kt=F3re_zwr=F3ci_nazw=EA_atrybutu=2C_kt=F3reg o?==?ISO-8859-2?Q?_warto=B6ci_spe=B3niaj=B1_zadany_warunek?=
=?ISO-8859-2?Q?Narz=EAdzie_do_budowania_zapyta=F1_SQL=2C?==?I SO-8859-2?Q?_PL/PgSQL=2C_PL/SQL=2C_T-SQL?=
[ms sql] =?ISO-8859-2?Q?wy=B6wietlenie_pierwszych_5_rekord?==?ISO-8859-2?Q?=F3w_z_zapytania_=3F_odpowiednik_ROWNUM_w_o?== ?ISO-8859-2?Q?raclu_dla_MS_SQL=27a?=
phpMyAdmin zadaje =?ISO-8859-2?Q?z=B3e_zapytania=2E=2E=2E_Dl?==?ISO-8859-2?Q?aczego=3F?=
[MSSQL2000] Problem z =?ISO-8859-2?Q?tabel=B1/indeksem/zapytanie?==?ISO-8859-2?Q?m_czy_b=B3=B1d_w_bazie_danych=2E=2E=2E?=
[mysql 5.x] jak =?ISO-8859-2?Q?zrealizowa=E6_zapytanie=3F_cz?==?ISO-8859-2?Q?yli_podzapytanie_i_wi=EAcej_ni=BF_jeden_rz=B1? ==?ISO-8859-2?Q?d_wynik=F3w?=
[oracle] zapytanie dynamiczne z =?ISO-8859-2?Q?=22dynamiczn=B1_?==?ISO-8859-2?Q?nazw=B1_tabeli=22?=
[mysql/php] jak =?ISO-8859-2?Q?zliczy=E6_ilo=B6=E6_unikalnyc?==?ISO-8859-2?Q?h_rekord=F3w_w_jednym_zapytaniu=3F?=
[MySQL] Zapytanie z =?ISO-8859-2?Q?dw=F3ch_tabel_na_raz_?==?ISO-8859-2?Q?i_grupowanie_po_wsp=F3lnym_polu=2E_Jak_=3F?=
mecze sie i mecze i nic - zapytanie czesciowe
zanotowane.pldoc.pisz.plpdf.pisz.plquentinho.opx.pl
Cytat
Decede mihi sole - nie zasłaniaj mi słonca. Gdy kogoś kochasz, jesteś jak stworzyciel świata - na cokolwiek spojrzysz, nabiera to kształtu, wypełnia się barwą, światłem. Powietrze przytula się do ciebie, choćby był mróz, a ty masz w sobie tyle radości, że musisz ją rozdawać wokoło, bo się w tobie nie mieści Hoc fac - tak czyń. A tergo - od tyłu; z tyłu. I czarne włosy posiwieją. Safona |
|