Hierarchikus adatkezelés SQL-lel PHP-ben II.
Az előző részt folytatva a hirearchikus adatkezelést boncolgatjuk tovább, azaz hogyan lehet hierarchikus adatainkat hatékonyan tárolni adatbázisban. Előző cikkemben egy jól használható, ugyanakkor nem eléggé hatékony megoldást mutattam be az adatok tárolásával kapcsolatban. Most újra megnézzük a problémát, de egy jóval hatékonyabb és kezelhetőbb megoldást láthatunk, elkerülve a processzor és memóriaigényes rekurziót, nem is szólva arról, hogy az adatbázis lekérdezések számát is radikálisan csökkentjük.
Minden elemhez társítani fogunk egy jobb illetve bal értéket a következő algoritmusnak megfelelően.
A szemléletesség érdekében íme az ábra grafikusan:
Jobban áttekintve az ábrát pár érdekes matematikai összefüggést vehetünk észre.
Mint látjuk jelen esetben 3 értéket tárolunk az adatbázisunkban:
Ez az osztály a következő függvényeket ismeri:
Azért hatékony ez a megoldás, mert összesen két lekérdezést kell tennünk, nem úgy mint a rekurzív megoldásban, ahol minden egyes elemnél kellett egy lekérdezés.
A $right verem arra szolgál, hogy lépést tartsunk abban, hogy éppen hol is tartunk a fastruktúrában. Azaz a verem a gyökértől kezdve mindig tartalmazza az aktuális ágat, amit éppen vizsgálunk, a gyökértől az aktuális levélig tartalmazva az elemeket.
Ennek a függvénynek létezik a jobban használahtó változata is, mégpedig aMaga a működés az előzőhöz hasonló, azonban az elemek nem egy stringbe, hanem egy tömbbe kerülnek. Ezt a működést szolgálja a
AzÁlljon itt egy megjelenítő függvény, ami megmutatja, hogy hogyan is lehet az ilyen típusú tömböt megjeleníteni rendezetlen listában. Ehhez feltételezzük, hogy egy Noha ez a függvény rekurzív, viszonylag gyorsan lefut, mivel csak referenciákat ad át, és vissza is csak az ábrázolandó stringet adja. A rekurzió azért szükséges, mert maga a tömb amivel dolgozik már tartalmazza a fastuktúrát, és a teljes bejárás tudtommal csak rekurzívan kivitelezhető.
Mivel tudjuk hogy a leszármazottak bal és jobb értékei az elem bal és jobb értéke közé esnek, ezért csak ezen elemek lekérdezésére van szükségünk, ugyanakkor szükségünk van az elemünk mélységére is a fában, és a leszármazottak mélységére is. A
Egy $id-jű elem leszármazottainak számát a
Elemünk elérhetőségi útját a
A
A
Az új elem beszúrásakor, a szülő elemtől jobbra levő elemek jobb és bal értéke tehát kettővel nő, és mivel a szülő elem jobb értéke nagyobb mint a bal, ezért az szintén megnő, ahogy szerettük is volna, hogy a fa szerkezetét egyensúlyban tartsuk.
Már csak be kell szúrni az új elemet, aminek bal értéke a szülő eredeti bal értéke lesz, jobb értéke, pedig a bal+1. Ezen műveleteket foglalja magába aA törlés előtt leeléenőrizzük, hogy az elem létezik-e, és hogy levél-e, majd töröljük és 1-et adunk vissza. Ha nem levél, akkor -1 el térünk vissza, míg ha az elem nem is létezik, akkor 0-val.
■ A sorozatban megjelent
- Hierarchikus adatkezelés SQL-lel PHP-ben I.
- Hierarchikus adatkezelés SQL-lel PHP-ben II.
- Hierarchikus adatkezelés SQL-lel PHP-ben III.
Egy kis elmélet
Mielőtt az új módszerhez tartozó kódot bemutatnám, tekintsük át a hozzá tartozó elméletet. Vegyük az előző példabeli faszerkezetet, de most a jobb áttekinthetőség végett vízszintesen ábrázolva.Programozás
+------------------------------------------+
PHP HTML
+-----------------+ +---------+
Stringkezelés PEAR CSS XHTML
+---------+ |
Mail HttpClient kiválasztók
+------------------------------------------+
PHP HTML
+-----------------+ +---------+
Stringkezelés PEAR CSS XHTML
+---------+ |
Mail HttpClient kiválasztók
Minden elemhez társítani fogunk egy jobb illetve bal értéket a következő algoritmusnak megfelelően.
- Számot növeljük 1-gyel.
- Az aktuális elem bal értékét beállítjuk a számra.
- Ha létezik az aktuális elemnek gyereke, akkor az lesz az aktuális elem, és folytatás az 1. lépéssel.
- Számot növeljük 1-gyel.
- Az aktuális elem jobb értékét beállítjuk a számra.
- Ha nincs az aktuális elemnek testvére, akkor vesszük a szülőt mint aktuális elemet, majd folytatjuk a 4. lépéssel.
- Vesszük az aktuális elem testvérét, mint az aktuális elemet, majd folytatjuk az 1. lépéssel.
1 Programozás 20
+-----------------------------------------------+
2 PHP 11 12 HTML 19
+------------------+ +---------+
3 Stringkezelés 4 5 PEAR 10 13 CSS 16 17 XHTML 18
+---------+ |
6 Mail 7 8 HttpClient 9 14 kiválasztók 15
+-----------------------------------------------+
2 PHP 11 12 HTML 19
+------------------+ +---------+
3 Stringkezelés 4 5 PEAR 10 13 CSS 16 17 XHTML 18
+---------+ |
6 Mail 7 8 HttpClient 9 14 kiválasztók 15
A szemléletesség érdekében íme az ábra grafikusan:
1. ábra
Jobban áttekintve az ábrát pár érdekes matematikai összefüggést vehetünk észre.
- Azon elemek tartoznak egy meghatározott elem alá, melyek bal és jobb értékei az elem bal és jobb értéke közé esnek.
- A bal és jobb értékek közül az egyik páros, a másik páratlan.
- Az elem gyerekeinek száma
(jobb-bal-1)/2
.
- Egy elem levél, ha bal értéke egyel kisebb, mint a jobb (jobb-bal=1).
Az adatbázis
Az előzőek mintájára újfajta struktúrájú adatbázist kell létrehoznunk az adatok tárolására.
CREATE TABLE my_table (
id int(10) unsigned NOT NULL auto_increment,
lft int(10) unsigned NOT NULL default '0',
rgt int(10) unsigned NOT NULL default '0',
PRIMARY KEY (id),
KEY lft (lft),
KEY rgt (rgt)
)
- id: az elem azonosítója, ami egy auto_increment mező, hogy új elemek hozzáadása esetén automatikusan új értéket kapjon
- lft: az elem bal értéke (azért nem használom a left értéket, mert az az SQL-ben foglalt szó akár csak a right)
- rgt: az elem jobb értéke
A Tree osztály
Az egész adatbázis kezelésére létrehoztam egy Tree nevű osztályt, melynek konstruktora csak az adatbázis nevét kéri.Ez az osztály a következő függvényeket ismeri:
retrieve($id, $indent=' ', $newLine="\n")
retrieve2array($id)
_addToArray(&$array, $depth, $item)
pathTo($id)
descendants($id)
getDepths()
getDepth($depth=1)
getDirectDescendants($id)
addNode($id)
leaf($id)
delNode($id)
moveNode($id, $pid)
retrieve2array($id)
_addToArray(&$array, $depth, $item)
pathTo($id)
descendants($id)
getDepths()
getDepth($depth=1)
getDirectDescendants($id)
addNode($id)
leaf($id)
delNode($id)
moveNode($id, $pid)
Lekérdezések
A retrieve az előző cikkben tárgyaltdisplayChildren
függvényt testesíti meg, csak új formában.<?php
// Visszaadja a fa formázott stringjét az $id-ben megadott gyökér alapján
function retrieve($id, $indent=' ', $newLine="\n") {
$output = '';
// lekérdezzük a bal és jobb értékét a gyökér elemnek
$result = mysql_query('SELECT lft, rgt FROM '.$this->table.
' WHERE id='.$id);
if($row = mysql_fetch_assoc($result)) {
// a $right tömb egy verem lesz
$right = array();
// most lekérdezzük az elem összes leszármazottját
$query = 'SELECT id, lft, rgt FROM '.$this->table.
' WHERE lft BETWEEN '.$row['lft'].' AND '.
$row['rgt'].' ORDER BY lft ASC';
$result = mysql_query($query);
// megjelenítjük az összes sort
while ($row = mysql_fetch_array($result)) {
// csak akkor ellenőrízzük a vermet, ha nem üres
if (count($right)>0) {
// ellenőrizzük, hogy ki kell-e venni az elemet a veremből
while ($right[count($right)-1]<$row['rgt']) {
array_pop($right);
}
}
// megjelenítjük az elemet a megfelelő indent-tel
$output.=str_repeat($indent,count($right)).$row['id'].$newLine;
// hozzárakjuk ezt az elemet a veremhez
$right[] = $row['rgt'];
}
}
return $output;
}
?>
Fa tömbben ábrázolása
A függvény működése elég trükkös. Először is vesszük a gyökér elem jobb és bal értékét, hogy tudjuk mely elemek tartoznak ezen elem alá, majd lekérdezzük ezen elemeket.Azért hatékony ez a megoldás, mert összesen két lekérdezést kell tennünk, nem úgy mint a rekurzív megoldásban, ahol minden egyes elemnél kellett egy lekérdezés.
A $right verem arra szolgál, hogy lépést tartsunk abban, hogy éppen hol is tartunk a fastruktúrában. Azaz a verem a gyökértől kezdve mindig tartalmazza az aktuális ágat, amit éppen vizsgálunk, a gyökértől az aktuális levélig tartalmazva az elemeket.
Ennek a függvénynek létezik a jobban használahtó változata is, mégpedig a
retrieve2array
:<?php
/* A fát adaja vissza a $id gyökértől kezdve egy tömbben.
Minden mélység 0. eleme maga a szinten lévő elem és a többi tömb
az elem mellett pedig a gyerekei.
*/
function retrieve2array($id) {
$arr = array();
// lekérdezzük a bal és jobb értékét a gyökér elemnek
$result = mysql_query('SELECT lft, rgt FROM '.$this->table.
' WHERE id='.$id);
if($row = mysql_fetch_assoc($result)) {
// a $right tömb egy verem lesz
$right = array();
// most lekérdezzük az elem összes leszármazottját
$query = 'SELECT * FROM '.$this->table.
' WHERE lft BETWEEN '.$row['lft'].' AND '.
$row['rgt'].' ORDER BY lft ASC';
$result = mysql_query($query);
// minden sort belepakolunk a $arr tömbbe
while ($row = mysql_fetch_assoc($result)) {
// csak akkor ellenőrízzük a vermet, ha nem üres
if (count($right)>0) {
// ellenőrízzük, hogy ki kell-e venni az elemet a veremből
while ($right[count($right)-1]<$row['rgt']) {
array_pop($right);
}
}
// hozzáadjuk az új elemet a $arr tömbhöz
$this->_addToArray($arr, count($right), $row);
// hozzáadjuk az elemet a veremhez
$right[] = $row['rgt'];
}
return $arr[0];
}
return $arr;
}
?>
_addToArray
függvény.Az
_addToArray
függvény három paramétert vár: a tömböt, amihez pakolni kell, a mélységet, ahova az elemet rakni kell és magát az elemet.<?php
/*
Segédfüggvény
Létrehoz egy új elemet a tömbben a $depth mélységben és
azt az $item-re állítja
*/
function _addToArray(&$array, $depth, $item) {
// előkészítünk egy ideiglenes referenciát a tömbre a bejárásához
$arr=&$array;
// bejárjuk a tömböt, és a referenciát a $depth mélységben
// levő utolsó elemre állítja, ha az nem üres
$i=0;
while($i<$depth && $c=count($arr)) {
$arr=&$arr[$c-1];
$i++;
}
// készítünk egy új tömböt a referencia tömb végén
$arr[]=array();
// hozzáadjuk az új elemet a tömbhöz
$arr[count($arr)-1][]=$item;
}
?>
name
nevű mezőt is tartalmaz az előbbi adatbázis.ALTER TABLE my_table ADD name VARCHAR(50)
<?php
// $arr a tömb, amit ábrázolni akarunk, pid az elem szülője,
// $first pedig, hogy kell-e a gyökér elem
function makeUL(&$arr, $pid, $first=true) {
$output='';
// ha nem ez az első elem, akkor kirakjuk az LI-t és az elem nevét
if(!$first) {
$output='<li>'.$arr[0]['name'];
}
$c=count(&$arr);
// ha léteznek az elemnek gyerekei, azokat is ábrázolni kell
if($c>1) {
$output.="\n<ul>\n";
// ábrázoljuk az egyes gyerekeket, úgy hogy rekurzívan
// végigmegyünk rajtuk
for($i=1; $i<$c; $i++) {
$output.=makeUL(&$arr[$i], $arr[0]['id'], false);
}
$output.="</ul>\n";
}
if(!$first) {
$output.="</li>\n";
}
return $output;
}
include_once('classes/tree.class.php');
$mytable=new Tree('my_table');
$myarray=$mytable->retrieve2array(1);
// ha meg akarjuk mutatni a gyökér elemet is, akkor ki kell rakni
// az UL elemeket
echo("<ul>\n");
echo(makeUL($myarray, 1, false));
echo("</ul>\n");
// ha nem kell a gyökér elem, akkor
echo(makeUL($myarray, 1));
?>
Közvetlen leszármazottak
A közvetlen leszármazottak lekérdezéséhez már bonyolultabb SQL műveletek szükségesek.Mivel tudjuk hogy a leszármazottak bal és jobb értékei az elem bal és jobb értéke közé esnek, ezért csak ezen elemek lekérdezésére van szükségünk, ugyanakkor szükségünk van az elemünk mélységére is a fában, és a leszármazottak mélységére is. A
getDirectDescendants
függvény pont ezen műveleteket végzi el:<?php
// Visszaadja az $id azonosítójú elem közvetlen leszármazottait
// egy asszociatív tömbben (id => resultset)
function getDirectDescendants($id) {
$results=array();
// lekérdezzük a mélységét és a jobb ill. bal értékét az elemnek
$query = 'SELECT T1.id, T1.lft, T1.rgt, COUNT(T1.id) AS depth FROM '.
$this->table.' AS T1, '.$this->table.
' AS T2 WHERE T1.lft BETWEEN T2.lft AND T2.rgt AND T1.id='.
$id.' GROUP BY T1.id ORDER BY T1.lft';
$result = mysql_query($query);
if($row = mysql_fetch_assoc($result)) {
// lekérdezzünk az elemünk alá eső elemeket, melyek
// mélysége egyel nagyobb, mint az elem mélysége
$query = 'SELECT T1.*, COUNT(T1.id) AS depth FROM '.
$this->table.' AS T1, '.$this->table.
' AS T2 WHERE T1.lft BETWEEN T2.lft AND T2.rgt AND T1.lft BETWEEN '.
$row['lft'].' AND '.$row['rgt'].
' GROUP BY T1.id HAVING depth='.($row['depth']+1).
' ORDER BY T1.lft';
$result = mysql_query($query);
while($row = mysql_fetch_assoc($result)) {
$results[$row['id']] = $row;
}
}
return $results;
}
?>
Egyéb lekérdezések
A különböző megjelenítések során szükségünk lehet más fajta lekérdezésekre is.Egy $id-jű elem leszármazottainak számát a
descendants
függvényel tudhatjuk meg, amely a már korábban említett matematikai összefüggést használja, mely szerint leszármazottak_száma=(jobb-bal-1)/2
.Elemünk elérhetőségi útját a
pathTo
függvény szolgáltatja. A függvény kihasználja a jobb és bal értékek alakulását, miszerint a szülő bal értéke kisebb, mint az elem bal értéke, a jobb értéke pedig nagyobb.A
getDepth
függvény az egy adott mélyégben levő elemeket szolgáltatja, függetlenül a szülőjüktől, a getDirectDescendants
mélység lekérdezését használva.A
getDepths
függvény az $id elemtől, mint gyökértől kezdve adja vissza az egyes elemek mélységét a fastruktúrában.A fa módosítása
Elsőként kezdjük egy, a módosítások szempontjából létfontosságú fügvénnyel, mégpedig amelyik megmondja, hogy egy adott elem levél-e. Ez az függvény aleaf
. Igaz értéket ad vissza, ha egy elem levél - azaz a jobb-bal==1, hamisat, ha az elem nem létezik, illetve nem levél.Elem hozzáadása a fához
Mint láthattuk elég sok matematikai összefüggés van a fa bal és jobb értékét figyelembe véve. Ezt fogjuk kihasználni új elem hozzáadásakor is. Mielőtt egy új elemet szúrnánk be a fába, módosítani kell a szülő elem bal érték minusz 1-nél nagyobb jobb és bal értékű elemeket a fában, méghozzá 2-vel kell megnövelni ezen elemek bal és jobb értékét.Miért pont 2?
Ha ránézünk a fastruktúránkra, akkor látjuk, hogy minden elem pontosan 2-vel növeli meg a jobb és bal értékek összegét. Azaz ha egy új elemet kell berakni, akkor szintén ennyivel kell megnövelni az elemek jobb illetve bal értékét.Az új elem beszúrásakor, a szülő elemtől jobbra levő elemek jobb és bal értéke tehát kettővel nő, és mivel a szülő elem jobb értéke nagyobb mint a bal, ezért az szintén megnő, ahogy szerettük is volna, hogy a fa szerkezetét egyensúlyban tartsuk.
Már csak be kell szúrni az új elemet, aminek bal értéke a szülő eredeti bal értéke lesz, jobb értéke, pedig a bal+1. Ezen műveleteket foglalja magába a
addNode
függvény, mely az újonnan beszúrt elem id-jét adja vissza, vagy -1-et, ha nincs meg a szülő.<?php
function addNode($id) {
if($id) {
$result = mysql_query('SELECT lft, rgt FROM '.$this->table.
' WHERE id='.$id);
if($row = mysql_fetch_assoc($result)) {
$result = mysql_query('UPDATE '.$this->table.
' SET rgt=rgt+2 WHERE rgt>'.
($row['rgt']-1));
$result = mysql_query('UPDATE '.$this->table.
' SET lft=lft+2 WHERE lft>'.
($row['rgt']-1));
$result = mysql_query('INSERT INTO '.$this->table.
' (lft, rgt) VALUES('.$row['rgt'].
', '.($row['rgt']+1).')');
return mysql_insert_id();
}
else {
return -1;
}
}
else {
return -1;
}
}
?>
Elem törlése a fából
Az elem törlése hasonlóképpen működik, mint az elem hozzáadása csak éppen kivonni kell.<?php
function delNode($id) {
if($id) {
$result = mysql_query('SELECT lft, rgt FROM '.$this->table.
' WHERE id='.$id);
if($row = mysql_fetch_assoc($result)) {
// a sor megvan, megnézzük, hogy levél-e, majd törlünk
if($row['lft']+1==$row['rgt']) {
$result = mysql_query('UPDATE '.$this->table.
' SET rgt=rgt-2 WHERE rgt>'.
($row['rgt']-1));
$result = mysql_query('UPDATE '.$this->table.
' SET lft=lft-2 WHERE lft>'.
($row['rgt']-1));
$result = mysql_query('DELETE FROM '.$this->table.
' WHERE id='.$id);
return 1;
}
else {
return -1;
}
}
}
// nem találtuk meg az elemet, vagy nem levél
return 0;
}
?>
Elem mozgatása
Az elem mozgatása a fenti két művelet kombinálása. Megvizsgáljuk, hogy létezik-e az új szülő ($pid), és hogy az elemünk levél-e. Ha ezen feltételek teljesülnek, akkor előbb az elemtől eredetileg jobbra levő elemek bal és jobb értékét 2-vel csökkentjük, majd a szülőtől jobbra eső elemek bal értékét pedig 2-vel növeljük, végül beállítjuk az elem jobb és bal értékét.<?php
// az $id elemet mozgatja az $pid elem alá
function moveNode($id, $pid) {
if($id && $pid) {
// vesszük az elemet
$result = mysql_query('SELECT lft, rgt FROM '.$this->table.
' WHERE id='.$id);
if($row = mysql_fetch_assoc($result)) {
// megnézzük levél-e
if($row['lft']+1==$row['rgt']) {
// tároljuk az eredeti bal értéket
$orgt=$row['rgt'];
// vesszük a szülőt
$result = mysql_query('SELECT lft, rgt FROM '.$this->table.
' WHERE id='.$pid);
if($row = mysql_fetch_assoc($result)) {
// megvan a szülő, frissítjük az elemeket
// eredetitől jobbra levők
$result = mysql_query('UPDATE '.$this->table.
' SET rgt=rgt-2 WHERE rgt>'.
($orgt-1));
$result = mysql_query('UPDATE '.$this->table.
' SET lft=lft-2 WHERE lft>'.
($orgt-1));
// szülőtől jobbra levők
$result = mysql_query('UPDATE '.$this->table.
' SET rgt=rgt+2 WHERE rgt>'.
($row['rgt']-1));
$result = mysql_query('UPDATE '.$this->table.
' SET lft=lft+2 WHERE lft>'.
($row['rgt']-1));
// a kívánt elemünk
$result = mysql_query('UPDATE '.$this->table.' SET lft='.
$row['rgt'].', rgt='.($row['rgt']+1).
' WHERE id='.$id);
return 1;
}
}
}
}
return 0;
}
?>
children vs direct desc
Nem teljesen sikerult meg megerteni a dolgot:
1.A children es a direct descendent miben kulonbozik?
2.Hogy adja vissza a count(T1.id) a melyseget?
Re: children vs direct desc
COUNT
pedig már a felmenők számát adja vissza, plusz az aktuálisan megjelölt elemet.Más megoldások
Aretrieve
ésretrieve2array
metódusban használt módszer helyett próbálkoztam direkt SQL-es megoldással is, de sokszori tesztelés után is lassabbnak bizonyult.A közvetlen SQL-es megoldás az indent számításra, ha már a $lft és a $rgt értékek megvannak
Teszt
Összehasonlítottam a két metódus időigényét. Mindkét esetben elvégeztem lekérdezést végeztem véletlenszerű mélységből:Időigények ezer lekérdezés esetén 10enként átlagolva:
Egyes kiugró esetekben a különbség akár 10-szeres is lehet a függvényekben használt metódus előnyére.
--------
Poetro
mélységi szám
Masrészt akkor ha jól okoskodom akkor egy nem rekurzív algoritmussal is lehetne pl egy UL listát generálni.
Az UL listában a forrásban pont a balszámok szerinti sorrendben vannak az elemek, csak azt kell tudnunk a kiiráskor, hogy a következő elem az gyereke, testvére vagy valamelyik felmenőhőz kapcsolodó gyereke. Ezt a mélységi szám alapján megtudjuk. Ha mélységi szám +1 akkor gyereke, ha = akkor testvér, ha kisebb akkor ős gyereke.
Vegyük az adott fát és írjuk ki a left értékek sorrendjében egy tömbbe, de jegyezzük fel minden elemhez a mélységi számot is.
pl:
programozas 1
php 2
string 3
pear 3
mail 4
httpclient 4
html 1
...
- nyit UL
ismetel:
{
- nyit LI
- kiir item
- ha van kovetkezo es melysegiszáma(m2) {
ha m2 < m1 akkor
(m2-m1) -szer zaro LI /* be kell zarni felmenoket m2-ig */
ha m2 = m1 akkor zaro LI
}
}
- utolso m-szer zar LI.
- zar UL
Meg nem probaltam ki, ugyhogy lehet nem jo, vagy hias.
Rekurzió nélküli megjelenítés
retrieve2array
függvényt alakítottam át a következőképpen:_depth
), az elemek továbbra is megfelelő sorrendben lesznek.A megjelenítéshez pedig fejlesztettem egy aranyos kis függvényt. Kettő paramétert vár. Az első az imént előállított tömbb, a második, pedig, hogy mi az elem neve a tömbben, amit ki kell írni.
Itt van hozzá egy minta, amin teszteltem:
"1","0","55","MENU"
"2","1","2","címoldal"
"3","3","4","hírek"
"4","5","14","tanszék"
"5","15","30","info"
"6","31","40","szakirány"
"7","41","42","klub"
"8","43","54","állás"
"9","6","7","munkatársak"
"10","8","9","eredmények"
"11","10","11","támogatók"
"12","12","13","kontakt"
"13","16","17","tantárgyak"
"14","18","19","tematika"
"15","20","21","tételsor"
"16","22","23","diploma"
"17","24","25","letöltés"
"18","26","27","jegybeiratás"
"19","28","29","eredmények"
"20","32","33","ismeret"
"21","34","35","nappali"
"22","36","39","UD"
"23","44","45","keres"
"24","46","53","kínál"
"25","47","52","Untitled"
"26","48","49","Untitled"
"27","50","51","Untitled"
"28","37","38","Untitled"
Poetro
a baja a modszernek:(
bajok baja?
A 2-t kell kombinalni:
Van ez a nestedset megoldas, es van a hagyomanyos.
A nestedset-es be be kell tenni egy parent_id mezot.
Azzal mar sokkal tobb mindent el tudsz intezni...
Sot, meg egy tree_id mezot bele, es akkor tobb fa is lehet egy db-ben.
(mondjuk szalakba szedett forum hozzaszolasok). Ekkor csak konkret fa-ra kell a rebuild-et ertelmezni egy uj elem beszurasakor.
--
[ Dönci ]
mondjuk 1 latogatott forumnal
Miert is van 3 tree?
Egy tree van csak egy forum topichoz! csak annyi a kulonbseg, hogy:
Main tree
topic1 tree
topic2 tree
stb.
Akkor ha a topic2 tree-be van beszuras, akkor csak azt az egyet kell updatelni, a tree2-t nem.
Mig ha ugy szervezed, hogy:
Forum
Main topic cat
Sub topic cat
Topic 1
Topic 2
Sub topic cat
Main topic cat
:
:
Akkor mindent kell update-lni.
Ezert celszeru tree_id-t is bevezetni, ami azonositja azt a fat.
Illetve, en betettem meg egy level azonositot is. Akkor lehet tudni, hogy eppen milyen melyen jarok a faban. Jo is az! :)
--
[ Dönci ]
A level-t amugy is tudod leke
kérdés
Ezért, hogy a fában szerepl
--------
Poetro
kérdés
innodb + tranzakció
Van valakinek tapasztalata, hogy mennyire megterhelő ez a mysql + innodb + tranzakció hármasnak több ezres rekordszámnál?
Illetve mennyivel hatékonyabb az első változatnál?
És mi van, ha nincs tranzakciókezelés, és mondjuk valakik egyidőben szúrnak be? Én inkább itt látom a gyenge pontot...
meg se forduljon a fejekben!
A tranzakció(kezelés) alapfogalom és követelmény az adatbázisok világában, ezért ezt a felvetés jobb mielőbb elfelejteni.
bocs
nem kötözködni akarok, de a mysql-ben csak innodb meg bdb tábla formátumban van tranzakciókezelés, ami szerintem ennél a példánál elengedhetetlen
úgyhogy szvsz egyről beszélünk
sotetbarna
Igen?
Mondjuk szerintem az ilyen adatkezelesek jellemyo felhasznalasi terulete a menukezeles, amit altalaban egy ember szokott vegezni, igy nem nagyon lesznek ilyen osszeakadasos problemak.
Es meg valami. En mar eleg sok helyen dolgoztam, kulonbozo fejlesztesi temakban, de meg sehol nem lattam, hogy valahol tranzakciokat hasznaltak volna. Foleg php-s fejleszteseknel. Ez nem azt jelenti, hogy ez igy jo, de - sajnos - igy van.
Persze az sem veletlen, hogy a php5 az sqllite-ot tamogatja, mert a felhasznalok 99%-a nem hasznalta masra a mysql-t mint egyszeru adatok beirasara es kiolvasasara, amire az sqllite tokeletesen megfelel, es meg csak agy mysql-t sem kell hozza elinditani.
nem? nemtudom!
Mondjuk szerintem ha nagyobbacska forgalom van akkor celszeru a menut nem az sqlben tárolni, hanem statikusan, vagy kesselve, és csak akkor kérni a dbtől mikor változik, és akkor megint kesselni...
Es meg valami. En nem sok helyen dolgoztam meg de azert par helyen igen, de mindenhol volt tranzakciokezeles. Foleg phps fejlesztesekben dolgoztam.
Persze sz sem veletlen, hogy nem hasznalok mysqlt, hanem pgt, ha csak tehetem, mert azt szeretem... sqllite-ot meg meg sosem hasznaltam.
--
üdv: kmm...
Mázlista
szvsz a gyenge pont maga az i
Hatekonyabbnak mindenkepp hatekonyabb, legyen szo barmilyen adatbazisrol is, amennyiben a fa szerkezet valtozas joval kevesebb mint a fa struktura lekerdezese. Marpedig az esetek 99.99999%-ban ez a helyzet :)
MLM rendszer felhasználóinak tárolása
Egy MLM (Multi Level Marketing ) rendszer felhasználóit és kapcsolati rendszerét szeretném PHP + valamilyen adatbázis segítségével tárolni és kezelni.
A programom által megvalósítandó funkciókat nagyjából, kis kiegészítésekkel fedik a fenti függvények.
De most nem egy menü struktúráról, vagy kategória fáról, hanem nagyságrendre akár egy 10-100, 1000? szint mélységű kapcsolati rendszerekből álló, 10000-100000, 1000000? rekordszámú adatbázisról lenne szó.
Ez egy gyakorlatilag nulláról induló, folyamatosan növekvő adatbázis lenne. A kezdeti költségeket minimalizálni kell, így jó lenne, ha működhetne PHP+MySQL-el (olcsó szolgáltató) valamint egy amatőr programozóval (én).
Alkalmas-e a feladatra a fenti algoritmus és a MySQL?
Gyakori műveletek lennének:
- felhasználó regisztráció (levél beszúrás egy ágba)
- felhasználó belépés (felhasználó alá tartozó emberek számának lekérdezése, közvetlenül alatta lévő felhasználók listázása és az azokhoz tartozó felhasználók számának lekérdezése)
Ritkábban:
- ág lecsatolása és áthelyezése,
Én arra gondoltam, hogy bizonyos adatokat cachelve kezelnék. Pl. a felhasználó alatti felhasználók számát felírom egy mezőbe. Viszont ezeket felfelé rekurzívan frissíteni kell pl. egy ág lecsatolásnál.
Rettentő nagy favágásnak számítana a tranzakciókezelős problémát valamiféle lock fájl alkalmazásával áthidalni?
Lényegében erre csak regisztrációkor lenne szükség, és ág áthelyezésekor. Előbbi napi 1-10-100 alkalom, utóbbi talán napi 1-10 eset, de az is lehet, hogy ez manuális lenne.
Milyen nehézségek adódhatnak még?
PHP-ban sokmindent megvalósítottam már, de nem vagyok egy tapasztalt programozó.
Belevághatok-e, vagy vegyem tudomásul, hogy egy ilyen rendszer kidolgozása kemény dió, és nem kisfiúknak való?
Alkalmas
Az egyik dolog, ami fontos lehet, hogy mivel ha jól vettem ki, különböző típusú adatokat akarsz tárolni akkor magában a fában az értékként érdemes lenne a típust és a hivatkozandó elem azonosítóját tárolni, majd típus szerint hozzácsatolni a megfelelő elemet.
Persze ha azonos típusú elemekből áll a hierarchia akkor ilyen probléma nincsen, és akkor a kívánt metódusok már meg is vannak írva az osztályban. Már csak az ábrázolást kell neked hozzáfejlesztened.
--------
Poetro
nem kevél elem mozgatása
Ha így van akkor hogy lehet megoldani, hogy bármilyen elemet, akár egész ágakat áthelyezni ?
nem levél mozgatása
Ez akár a jelenlegi kód felhasználásával is történhet, hogy rekurzívan végigmegyünk az elemeken, és mozgatjuk őket.
Kisebb felkészüléssel persze megírható az ágak támozgatása, de ezt a feladatot a fejlesztőkre bízom. Ha előállok egy kész megoldással, akkor azt publikálni fogom.
--------
Poetro
Akkor megírom
ág mozgatása
Nekem van.
:)
Backup lemezen
Doctrine
Üdv,
Felhő
Működés...
re: ?
Üdv,
Felhő
Nyilván..
(Személy szerint én nem szeretek vakon használni valamit, amit nem ismerek...)
begyógyítani
ott a forrás ;)
Üdv,
Felhő
Fa mozgatása
Ez nem egészen az amit Te szeretnél valszeg, mivel ez URL kezelésre született, de sztem egy kis kisérletezéssel át tudod alakítani.
Ha van rá igény, az egész URL kezelő mechanizmusomat nyilvánossá teszem.
reméltem, hogy sql
Nemsokára kipróbálom.
CakePHP
A CakePHP1.2-ben van egy TreeBehavior, amely az ebben a cikkben is vázolt "modified pre-order tree traversal" módszert alkalmazza. Ez nem felel meg a céljaidnak?
Foxmulder
Hibás a megfogalmazás a
"módosítani kell a szülő elem bal érték minusz 1-nél nagyobb jobb és bal értékű elemeket a fában" -- szülő elem jobb érték minusz 1 helyesen.
"Már csak be kell szúrni az új elemet, aminek bal értéke a szülő eredeti bal értéke lesz, jobb értéke, pedig a bal+1." -- szintén szülő eredeti bal értéke helyesen.
errr még én is elírtam :)
Hiba
Warning: Call-time pass-by-reference has been deprecated - argument passed by value; If you would like to pass it by reference, modify the declaration of count(). If you would like to enable call-time pass-by-reference, you can set allow_call_time_pass_reference to true in your INI file. However, future versions may not support this any longer. in c:\appserv\www\tree\index.php on line 10
Warning: Call-time pass-by-reference has been deprecated - argument passed by value; If you would like to pass it by reference, modify the declaration of [runtime function name](). If you would like to enable call-time pass-by-reference, you can set allow_call_time_pass_reference to true in your INI file. However, future versions may not support this any longer. in c:\appserv\www\tree\index.php on line 17
Parse error: parse error in c:\appserv\www\tree\tree.class.php on line 299
Fatal error: Cannot instantiate non-existent class: table in c:\appserv\www\tree\index.php on line 27
Warning: mysql_fetch_assoc(): supplied argument is not a valid MySQL result resource in c:\appserv\www\tree\tree.class.php on line 284
Hibaüzenetek
Megkérnélek arra is, hogy írjad alá - bármilyen aláírással -, mert az Anonymousok beszélgetésének olvasgatása közben igen nehéz elkülöníteni, hogy ki kit kérdez.
-boogie-
forráskód
Az index.php-ben pedig a makeUL-van+a listázás
Használat
A letölthető állományban valóban van egy hiba. A 299. sorának végére egy záró zárójelet be kell tenni a pontosvessző elé.
Az
index.php
-ben ne feledkezz el az adatbázishoz csatlakozás mellett a használandó adattábla kijelöléséről.Nekem a hibajavítás után működik a kód. Valószínűleg nem ártana tudnunk, hogy milyen PHP-vel próbálod a dolgot. Én PHP 4.x-szel.
-boogie-
Itt az index.php
Elírás
-boogie-
Elírás
Igen
-boogie-
Még 2 hiba
Warning: Call-time pass-by-reference has been deprecated - argument passed by value; If you would like to pass it by reference, modify the declaration of count(). If you would like to enable call-time pass-by-reference, you can set allow_call_time_pass_reference to true in your INI file. However, future versions may not support this any longer. in c:\appserv\www\tree\index.php on line 13
Warning: Call-time pass-by-reference has been deprecated - argument passed by value; If you would like to pass it by reference, modify the declaration of [runtime function name](). If you would like to enable call-time pass-by-reference, you can set allow_call_time_pass_reference to true in your INI file. However, future versions may not support this any longer. in c:\appserv\www\tree\index.php on line 20
A 13. sor: $c=count(&$arr);
A 20. sor: $output.=makeUL(&$arr[$i], $arr[0]['id'], false);
A 20. sorban nyilván azért jelez, mert ott ismét meghívja azt a függvényt, amiben a 13. sor van.
<Nincs cím>
Kipróbáltam a másik listázást.
Csak az a baj, hogy a szerző néhol máshogy hivatkozik a dolgokra.
Pl:
-Tree helyett Table
-my_table helyett menu
-name helyett title
Nagyjából ennyi
PHP verzió?
-boogie-
<Nincs cím>
allow_call_time_pass_reference = Off
A Te rendszeredben a fenti beállítás van, míg Poetro-nál nem. Szóval nem konkrétan hibáról van szó, bár az alkalmazott technika már régóta depricated.
A megoldás, hogy a függvények definíciójában kell jelezni, hogy az adott változót referenciaként várja, nem pedig híváskor jelezzük ezt.
Felhő
kiegészítés
Először is megköszönném a cikk írójának, hogy időt és energiát fordított arra, hogy ezt az egészet összeállítsa nekünk, nekem nagyon sokat segít a munkámban.
Másodszor pedig kérnék valamit a tőlem okosabbaktól :)
Van egy getDepths() funkció, ami a fő ágtól számítva kéri le az összes ág mélységét. Ezt szerettem volna módosítani úgy, hogy a megadott ágtól kérje csak le a mélységeket, mert fölöslegesen sok adatot ad vissza, ha a root-tól kéri le. Nekem gyakran csak az egyik al-ág mélységeire van szükségem.
Leírnátok, hogy hogyan módosíthatom a cél érdekében ezt a funkciót?
Köszi!
Üdv
Áthelyezés Bug
1) Elem adatainak lekérése.
2) Elem leendő szülőjének a megtalálása, adatainak mentése.
3) Elem törlése a fából, amikor is a jobb és bal számok változnak, az ősöknél mindenképpen.
4) Elem beszúrása az eredetileg eltárolt szülő adatokhoz, amik azóta megváltozhattak a harmadik lépésben.
A rossz sorrend: Új szülő adatok mentése, törlés, beszúrás: