Autor Zpráva
Sir Tom
Profil
Ahoj, v MySQL mám tabulku "sections" se sloupci ID, name, level, parentID. Každá položka zde obsahuje level 1,2, nebo 3, kde 1 značí nejvrchnější level a 3 nejnižší a sloupec parentID, kde je napsáno k jaké položce je tato položka podřízena. Chci vypsat všechno záznamy tak, aby pod každou položkou, která je rodičem se objevily položky (potomci) o level nižší. Uvedu příklad:

Záznamy v tabulce:
ID,name,level,parentID;
1,"rok 2009",1,0; 
2,"rok 2010",1,0;
3,"rok 2011",1,0;
4,"duben",2,1;
5,"červen",2,2;
6,"2. pondělí",3,4;

Výstup by měl vypadat takto:
  rok 2009
    duben
      2. pondělí
  rok 2010
    červen
  rok 2011


Poradíte mi, prosím, nějaký pěkný MySQL dotaz? Napadlo mě použít 3 vnořené dotazy, ale to mi nepřijde jako efektivní metoda.
TomášK
Profil
Sir Tom:
Můžeš si vybrat z následujících řešení:
* vybrat z databáze vše a řešit to na úrovni aplikace
* změnit strukturu uložení dat (hledat traverzování kolem stromu)
* změnit databázi na nějakou, která umí rekurzivní dotazy

Pokud vím, na úrovni MySQL to při stávající struktuře nejde elegantně vyřešit.
Sir Tom
Profil
TomášK:
změnit strukturu uložení dat (hledat traverzování kolem stromu)
Děkuji, nastuduji tento způsob. O tomto jsem vůbec nevěděl, aspoň se dozvím něco nového. :)
Alphard
Profil
Teorie je např. na http://interval.cz/clanky/metody-ukladani-stromovych-dat-v-relacnich-databazich/
Prakticky potom nejlépe na http://php.vrana.cz/traverzovani-kolem-stromu-prakticky.php

Je to časný problém. Začátečníci vymyslí ukládání parent id (nebo to radí na nějakém známém webu?) a pak nevědí, jak dál...
TomášK
Profil
Alphard:
Je to časný problém. Začátečníci vymyslí ukládání parent id (nebo to radí na nějakém známém webu?) a pak nevědí, jak dál...

Nelíbí se mi, že z toho příspěvku vyznívá, že použití parent_id je špatný návrh databáze. Podle mě návrh pomocí parent_id je ten správný a používání odkazovaného uložení je workaround, jak to řešit, že databáze nemá rekurzivní dotazy. Přijde mi to, jak ukládání počtu položek, kdyby databáze neuměla efektivně COUNT(*). Analogie funguje i v tom, že někdy může být 'méně pěkné' řešení lepší z důvodu optimalizace i v databázi, která umožňuje obě možnosti (a rozdíl nebude velký, ani lft, rgt ani ruční ukládání počtu položek jsem snad zatím z optimalizačních důvodů nepoužil).
Alphard
Profil
TomášK:
Nelíbí se mi, že z toho příspěvku vyznívá...
Měl jsem si to asi odpustit :-), trochu jsem doufal, že právě zjistím příčinu.
99 % dotazů se tady týká MySQL a tazatelé s ním pracují, takže rekurzivní dotazy prostě nejsou k dispozici. Menší datové sady lze řešit rekurzivně s pomocí PHP, ale je jasné, že výkon klesá docela rychle. V dané situaci je traverzování kolem stromu asi nejlepší možnosti. Prostě denormalizace z praktických důvodů, nebudu stavět teoreticky dokonalou relační databázi, ze které nedostanu data (zřejmá nadsázka, nebrat doslova). Objektivní hodnocení jednotlivých metod je v odkázaném článku, včetně informací o tom, že přesunout uzly ve stromě není zrovna jednoduché.
okolojdouci
Profil *
Alphard:
Menší datové sady lze řešit rekurzivně s pomocí PHP, ale je jasné, že výkon klesá docela rychle.

Jen bych pro představu doplnil, že při té logice, která je uvedena v #1, je získání dat z databáze nepoužitelně pomalé už při 500 položkách - jde o sekundy.
TomášK
Profil
Alphard:
Souhlasím, že pro MySQL je to zřejmě nejlepší řešení - jak říkáš, denormalizace z praktických důvodů.

Nepřekvapuje mě, že struktura s parent_id je to, co navrhují ti, co na tenhle problém zatím nenarazili. Jedná se o běžné použití cizího klíče, jen je cílová tabulka shodná se zdrojovou. Pokud někdo používá cizí klíče, je tohle přesně struktura, která ho napadne. Traverzování kolem stromu nenapadne při prvním použití snad nikoho - pokud si o ní nepřečte článek.

okolojdouci:
Uvedená čísla jsou zřejmě pro hodně pomalou implementaci. Zkusil jsem si napsat kod, který vygeneruje strom - pole s položkami (id, parent_id), zamíchá ho a poté vypíše tak, jako se vypisuje menu. Včetně vygenerování a setřídění to na 1000 položkách trvá 0.03s. Pro 50 000 položek to trvá ~1sec.
okolojdouci
Profil *
TomášK:
Uvedená čísla jsou zřejmě pro hodně pomalou implementaci

Uf, tak to se omlouvám. Netestoval jsem to nějak podrobně, skládám z toho menu, které se rozbaluje javascripem, tak ta prodleva může vznikat tam.
Sir Tom
Profil
TomášK:
Traverzování kolem stromu nenapadne při prvním použití snad nikoho - pokud si o ní nepřečte článek.
Ano - předtím jsem to neznal. Jaksi jsem ale stejně počítal s tím, že id a parent_id nebude to pravé ořechové... navíc nic jiného mě nenapadlo a už vůbec ne, že bych měl do vyhledávače napsat "traverzování kolem stromu".

Ona tabulka bude mít max. 100 záznamů.

Alphard:
Díky za odkazy, ale již jsem si je našel předtím.

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:

0