Autor Zpráva
nowis
Profil
Zdravím,

vytvořil jsem takový, na mě dost komplikovaný dotaz. Nejsem žádný velký SQL přeborník a proto bych se rád zeptal vás, zkušenějších, co si o tom myslíte.

Jedná se o několik tabulek s různými informacemi, ale mají vždy společný sloupec ID_klient a ID_vec. Takové tabulky jsou 3 a spojuji je pomocí UNION.
4. tabulka ID_vec neobsahuje, ale dá se to spojit pomocí www, www1, www2, www3 nebo www4, protoze každý záznam v tabulce veci je identifikovatelná prostřednictvím url.
Pak mám databázi klientů a databázi věcí.

Chci se vás zeptat na to, jestli je dotaz logicky správně navržen a taky na to, jestli mám dobře vyřešeno spojení v JOINU ve 4. tabulce, kde je několik možností spojení řešeno pomocí OR. Zjistil jsem, že každý takový OR má značný vliv na výkon dotazu. Výsledek má asi 2500 řádků a dotaz trvá 17s, což je asi tak o 16 sekund více, než bych chtěl :-) Může být u tab4 problém i to, že je v jiné db, ikdyž na stejném serveru?

Dotaz vypadá zhruba takto (pro zjednodušení nevypisuji všechny sloupce dle reality):

SELECT * FROM ( 

SELECT tab1.datum as datum, kontakty.jmeno as klient, veci.nazev as nazev FROM tab1
LEFT JOIN veci on tab1.id_vec = veci.id
LEFT JOIN kontakty on tab1.id_klient = kontakty.id

UNION 

SELECT tab2.datum as datum, kontakty.jmeno as klient, veci.nazev as nazev FROM tab2
LEFT JOIN veci on tab2.id_vec = veci.id
LEFT JOIN kontakty on tab2.id_klient = kontakty.id

UNION 

SELECT tab3.datum as datum, kontakty.jmeno as klient, veci.nazev as nazev FROM tab3
LEFT JOIN veci on tab3.id_vec = veci.id
LEFT JOIN kontakty on tab3.id_klient = kontakty.id

UNION

SELECT tab4.datum as datum, tab4.jmeno as klient, veci.nazev as nazev FROM jinadb.tab4 as tab4 
INNER JOIN veci on tab4.www = veci.www OR tab4.www1 = veci.www OR tab4.www2 = veci.www OR tab4.www3 = veci.www OR tab4.www4 = veci.www
LEFT JOIN kontakty on tab4.id_klient = kontakty.id 
WHERE veci.www != ''''

GROUP BY tab4.id

) AS U 
ORDER BY datum DESC

Omlouvám se za případné syntaktické chyby, píšu to z hlavy bez testu. Jde mi spíše o logiku v návaznosti na výkon

Děkuji


Moderátor Alphard: Vkládej prosím kódy mezi značky [pre] a [/pre] (stačí kliknout na ).
Alphard
Profil
Jsou na těch www sloupcích aspoň indexy? Zkuste ten poslední select vykonat samostatně a kouknout na explain.
Jak moc to filtruje podmínka veci.www != '''', nepomohlo by nacpat ji přímo do joinu?
RastyAmateur
Profil
Alphard:
Jak moc to filtruje podmínka veci.www != '''', nepomohlo by nacpat ji přímo do joinu?
Jaký rozdíl je mezi tím, jestli je to za WHERE nebo v JOINu? Co se v jakých případech využívá a proč?
Alphard
Profil
RastyAmateur [#3]:
Teď sám hledám, jak přesně se chová MySQL plánovač a u inner joinu to výkonově bude zřejmě srovnatelné, takže rozdíl je jen semantický. Docela dobrá a stručná odpověď je na Object moved. Tipuji, že tazatel nechce spojovat tabulky podle prázdného řetězce, takže bych to dal spíše do joinu. Do where patří podmínky filtrující výsledná data bez vazby na spojení tabulek.
nowis
Profil
Alphard:
Indexy nebyly všude a pomohly

RastyAmateur:
S tím jsem si vyhrál dost, a žádny extra efekt to nemělo

Nakonec ale nejvíce zabralo, že jsem JOIN v poslední UNIONu rozdělil na 5 dalších UNIONů. Tedy namísto těch ORů v jednom JOINu jsem udělal samostané dotazy, každý s vlastním JOINem na jednotlivé sloupce tab4.www - tab4.www4. Snad jsem to popsal pochopitelně.

Ten výkonový rozdíl je fakt zásadní, z těch 17s jsem se dostal na 1,5s. Ten server není žádný HW zázrak, takže ten výsledek je pro mě naprosto dostatečný.

Děkuji
pcmanik
Profil
nowis:
Ide o to, že mysql nevie využiť index nad OR preto pozoruješ to rapídne navýšenie výkonu keďže sa všetky hodnoty nemusia filtrovať ale použije sa index.
RastyAmateur
Profil
pcmanik:
Mohl by jsi, prosím, ještě trošku jinak vysvětlit tvé zdůvodnění? Nějak jsem to nepochopil...
pcmanik
Profil
RastyAmateur:
Prepáč máš pravdu, keď si to po sebe znovu čítam moc to nedáva zmysel...

MySQL (neviem ako iné DB), nevie využiť index nad stĺpcami nad ktorými sa použije OR. Ani ak vytvoríš viac stĺpcový index tak sa nevie použiť a musia sa teda prejsť všetky riadky v tabuľke ktorá sa joinuje a pracne vyfiltrovať hodnoty. Preto je riešením použiť union, ktorý síce vykoná interne viac dotazov, avšak pri každom sa využije index a teda potrebný výsledný čas bude oveľa kratší.
A teraz si predstav ze k 1000 hodnotám pripájaš tabuľku ktorá má povedzme ďalsích 10000 riadkov, bez využitia indexu sa vykoná pre každý z 1000 hodnôt musí prejsť všetkých 10000 záznamov v druhej tabuľke (1000 * 10000). Ale ak použiješ riešenie cez UNION vykoná sa povedzme 1000 * 100 prejdení.
RastyAmateur
Profil
pcmanik:
Nejsem si jistý, jestli to chápu dobře, ale každopádně děkuji za vysvětlení.
nowis
Profil
pcmanik:

Připojuji se k poděkování za vysvětlení, mám teď pocit, jako bych se v SQL dostal na další metu :-)
Každopádně z toho plyne poučení, že když joinovat, tak vždy jednoznačně (konkrétně) a indexovaně.
pcmanik
Profil
nowis:
Každopádně z toho plyne poučení, že když joinovat, tak vždy jednoznačně (konkrétně) a indexovaně.
Pekne si to zhrnul ;) A najlepší kamarát je vždy EXPLAIN :)

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: