WN

WN (https://www.wn.se/forum/index.php)
-   Serversidans teknologier (https://www.wn.se/forum/forumdisplay.php?f=4)
-   -   PHP forum loop forum parents (https://www.wn.se/forum/showthread.php?t=1055199)

frecka 2012-10-02 18:52

PHP forum loop forum parents
 
ja har en tabell med id, forum_name, parent

om parent är 0 har den ingen förälder.

Om parent != 0 så är parent något forums id.

Om ja vill skriva ut ett forums alla föräldrar måste jag loopa någonstans för att kolla om den har en förälder.

Jag vill liksom få:

Spel->CS->Global Offensive->Skärmdumpar

Detta vill jag skriva ut genom att ha Skärmdumpars ID.

Där Global Offensive är skärmdumpars förälder
och cs är global offensive förälder
och spel är CS förälder.

Det är just denna loop där ja kollar om man har ngn parent som jag iunte vet hur ja ska bygga.

Conny Westh 2012-10-03 01:51

Typiskt rekursiv funktion, dvs funktionen kollar om parent är NULL, är den det så vet den att den nått ROOT-nivån i trädstrukturen, annars anropar den sig själv igen med parent som ID.

Pseudocode:
Kod:

function category(categoryID)
    if (categoryID==NULL)
        // Nu har vi nått root-nivån
    else
        // Slå upp den nya parentID för aktuell categoryID från SQL
        // Här kör du SQL-satsen....       

        // Sen anropar du category()-funktionen igen med parentID som parameter
        category(parentID)
    endif
End function


dAEk 2012-10-04 20:39

Samma sak kan göras direkt i SQL om du vill.

I MSSQL är det relativt billigt att skapa temptabeller vilket man tillsammans med en sp skulle använda för att skriva en rekursiv, ehm, sp. Vet inte hur det är i MySQL som jag misstänker att du använder dig av men det vet säkert någon annan här.

Min hjärna är dock inte tillräckligt vaken för rekursiva anrop och framförallt inte i SQL men du kan ju kika på följande länk: http://www.google.se/search?hl=sv&cl...sv&q=recursion ;)

Clarence 2012-10-04 21:43

Rekursiva funktioner är ofta långsamma. Behöver du räkna med svarstid från SQL för varje rekursion finns det än mer anledning att hålla sig borta från det.

Det finns något som heter "Nested set model" som är desto mer effektivt. Läs på t ex http://scvinodkumar.wordpress.com/20...data-in-mysql/

Eller googla på hierarchical data in MySQL (eller annan dbms) eller nested set model.

Ett alternativ är att denormalisera och spara hela sökvägen i varje post, men det blir istället ineffektivt och komplicerat vid uppdateringar.

dAEk 2012-10-04 22:56

Du, Clarence, verkar vara en av få tekniskt vassa på det här forumet men usch, jag har svårt för sådana här generella rekommendationer. Hur relevant är det i det här fallet egentligen?

Svaret är att vi inte vet. Eftersom frecka inte skriver hur många nivåer eller träd det handlar om kan vi bara göra antaganden och det är ju sällan särskilt givande.

Om man inte vet vad som är långsamt - mät. Antar eller gissar man kommer man garanterat sitta och pilla på nånting som inte ger någon mätbar skillnad. Den risken har man inte om man gör metodiska mätningar. Då får man det svart på vitt. Det kan inte gå fel! ;)

Conny Westh 2012-10-04 23:28

Ett annat tips vad gäller optimeringar är att man inte ska optimera om man inte behöver det. Dvs lös först problemet på ett pedagogiskt och enklet sätt, så det blir lätt för nästa programmerare som ska ta över koden att förstå vad som görs. Sen om det visar sig att det blir prestandaproblem, då och först då börjar man optimera för ökad prestanda. Den största kostnaden vid systemutveckling är inte CPU-tid utan programmerarnas arbetstid. det är med andra ord effektivare att optimera programmerarnas arbetstid än datorns CPU-tid.

Sen att rekursiva funktionsanrop skulle vara ineffektiva har jag aldrig sett, det är körningsmässigt och kodmässigt inget som skiljer ett vanligt funktionsanrop från ett rekursivt, de är identiskt effektiva i alla kompilerande språk (åtminstonde de jag kört och testat).

Clarence 2012-10-05 08:42

Till att börja med, efter man varit på tillräckligt antal dåligt skrivna webbapplikationer som använt rekursiva anrop med queries, förmodligen för att programmerare precis lärt sig rekursion, så blir man lite allergisk av det. Men med det sagt så är det precis lika träligt att i det fallet ha en vanlig loop med queries.

Annars har ConnyWesth rätt, förutsatt att man har en kompilator/språk med tail call recursion optimization och man skriver sin rekursion så att kompilatorn kan göra optimeringen - vilket väl i bästa fall görs 50% av gångerna även av kompetenta programmerare. Också förutsatt att du faktiskt behövde en loop eller rekursion från första början - vilket inte ens gäller i detta fallet.

Själv tycker jag väldigt bra om generella rekommendationer. Använd rekursion om det finns ett behov för rekursion - för läsbarhet, kodeffektivitet osv.

I fallet i denna tråden har man istället fått sämre läsbarhet och 300% extra frågor mot databasen (delen av stacken som är svårast att skala effektivt). Och då har man ens inte tagit i åtanke de andra negativa effekter man får om man t ex ska hämta och visa hela trädet, noder på samma nivå osv.

danjel 2012-10-05 13:45

Intressant länk med "Hierarchical Data" och "Nested set model".
Men också avancerat,
som jag förstår detta, med bibehållen tabellstruktur är alternativet selfjoins

SELECT t1.forum_name AS lev1, t2.forum_name as lev2, t3.forum_name as lev3, t4.forum_name as lev4
FROM forums AS t1
LEFT JOIN forums AS t2 ON t2.parent = t1.id
LEFT JOIN forums AS t3 ON t3.parent = t2.id
LEFT JOIN forums AS t4 ON t4.parent = t3.id
WHERE t1.forum_name= 'Spel' AND t4.forum_name= 'Skärmdumpar';

Personligen skulle jag dock gå på ConnyWesth alternativ i detta specifika fall,
om vi antar att man ska bygga ett eget enkelt forum.

Givet att det inte blir fler än ett par hundra forumkategorier kan man ju även hämta all data
och spara i en array, då blir det endast ett sql anrop

$query = 'SELECT * Forum;';
$result = MYSQL_QUERY($query);
$forums= array();
while($row = mysql_fetch_array($result)) {

// stoppa in data i $forums array
}

function category(categoryID)
if (categoryID==NULL)
// Nu har vi nått root-nivån
else
// Slå upp den nya parentID för aktuell categoryID
// hämta data från $forums array

// Sen anropar du category()-funktionen igen med parentID som parameter
category(parentID)
endif
End function

dAEk 2012-10-06 11:21

Vi har inte sett någon annan kod så de olika lösningarna som vi ställer mot varandra kan mkt väl vara en droppe i havet. Vi har heller ingen aning om vilka krav som finns ang. prestanda så det finns egentligen inget värde i att snacka optimering. Visst kan vi diskutera fram och tillbaka rent generellt men jag ser inte någon mening med det. Jag får iaf inte ut något av det.

Ang. läsbarhet: man behöver inte skriva ett blogginlägg för att förklara hur en rekursiv metod funkar (till skillnad mot t.ex. nested set-modellen) vilket borde ge en indikation på vilken av lösningarna som är enkel/tydlig/läsbar/etc. Rekursiva metoder är dock knepiga att få grepp om för en del :) så man får såklart välja en lösning som man förstår sig på. Eventuella prestandaproblem hanterar man när man vet vilka de är.

Clarence 2012-10-06 14:24

För mig handlar det om att skriva bestående och flexibel kod snarare än att skriva prototyp-kod som fungerar nu och kanske måste skrivas om senare.

En bra skriven sajt kan du flytta till olika miljöer. En dåligt skriven sajt som du flyttar till något moln, t ex Amazon, får du ofta skriva om från grunden för att svarstiderna inom molnet blir för stora. Rekursion eller loopar med onödiga queries är ett klassiskt exempel som kan leda till sekunder i svarstid och oftast inte tar någon nämnvärd ansträngning att bli av med. Skulle det bara gälla en meny så är det inte heller några problem att optimera vid flytten - men återupprepas samma ineffektiva mönster i övriga delar av applikationen så har man ett helvetes jobb framför sig.

dAEk 2012-10-06 15:32

Man kan såklart planera för olika driftmiljöer om man vill men det brukar inte vara lönt eftersom man sällan flyttar projekt till en annan miljö än den man utvecklat för. Man måste dra gränsen nånstans och koda utifrån det som är känt.


Notera att jag inte försvarar eller på något sätt tycker att loopar eller rekursiva anrop som kopplar upp sig mot en databas är något som borde finnas i produktion. Ville bara få det sagt eftersom jag inser att det inte framgår. Connys kodexempel skulle jag alltså inte använda mig av, dock kan man göra samma sak direkt i databasen istället.

Clarence 2012-10-06 17:44

Citat:

Ursprungligen postat av dAEk (Inlägg 20452367)
Man kan såklart planera för olika driftmiljöer om man vill men det brukar inte vara lönt eftersom man sällan flyttar projekt till en annan miljö än den man utvecklat för. Man måste dra gränsen nånstans och koda utifrån det som är känt.


Notera att jag inte försvarar eller på något sätt tycker att loopar eller rekursiva anrop som kopplar upp sig mot en databas är något som borde finnas i produktion. Ville bara få det sagt eftersom jag inser att det inte framgår. Connys kodexempel skulle jag alltså inte använda mig av, dock kan man göra samma sak direkt i databasen istället.

Annan driftmiljö är ett exempel, andra exempel är förändrad datamängd, förändrade datadjup, ökad trafik osv. Jag tycker inte att alla måste använda en nested set model de inte förstår. Men jag tycker att idiotiskt ineffektiv kod aldrig ska läggas i produktion om den inte är ett proof of concept/prototyp som är tänkt att skrivas om snarare än vidareutvecklas.

Att lägga hanteringen i databasen med en struktur såsom föreslagits i denna tråd är fullt acceptabelt - om databassystemet stödjer rekursiva frågor (som t ex MSSQL, Oracle och PostgreSQL gör, men inte MySQL). I MySQL blir det mest bara jobbigare och krångligare än att använda nested-set modellen - men det blir mer en smakfråga.

Att ha strukturen som i tråden och vid varje skrivoperation lägga in denormaliserade sökvägar i tabellen är också fullt acceptabelt.

frecka 2012-10-07 00:45

Tog efter din kod, den va enkel att förstå :)

function writeParents($fid) {
$check_parent = mysql_query("SELECT parent FROM forums WHERE id = {$fid} && parent != 0");
if(mysql_num_rows($check_parent) == 0):
return false;
else:
$new_parent = mysql_result($check_parent, 0, "parent");
writeParents($new_parent);
echo " - <a class='orange' href='".ROOT."forum.php?id=".mysql_result($check_p arent, 0, "parent")."'>". mysql_result(mysql_query("SELECT name FROM forums WHERE id = '".mysql_result($check_parent, 0, "parent")."'"), 0, "name")."</a>";
endif;
}

dAEk 2012-10-11 20:30

Kul att du löste ditt problem även om lösningen i sig i stort sett är den sämsta tänkbara varianten. Förstår att det blir som det blir när inte fler bidrar med konkreta kodexempel. Det är ju trots allt ett väldigt bra sätt att lära sig när man är nybörjare inom programmering. :)

dAEk 2012-10-11 20:39

Citat:

Ursprungligen postat av Clarence (Inlägg 20452375)
Men jag tycker att idiotiskt ineffektiv kod aldrig ska läggas i produktion om den inte är ett proof of concept/prototyp som är tänkt att skrivas om snarare än vidareutvecklas.

Ja, känner man till att det är stor skillnad på lösning a jämfört b är det väl dumt att medvetet välja den sämre så länge som det inte påverkar kodkomplexiteten. Fattar inte riktigt vilken kod du syftar på eftersom du citerar mig utan att jag har lagt upp någon kod, men jag börjar ledsna på den här ändlösa diskussionen. Man gör det som man tycker är bra, gör kunden nöjd och får systemen att snurra stabilt under de förutsättningar man har.


Citat:

Ursprungligen postat av Clarence (Inlägg 20452375)
Att ha strukturen som i tråden och vid varje skrivoperation lägga in denormaliserade sökvägar i tabellen är också fullt acceptabelt.

Absolut. Det är en poäng som många nog glömmer bort emellanåt. Jag vet iaf att jag glömmer bort platta/förberedda tabeller titt som tätt och istället snöar in på att få systemen så "rena" som möjligt. Lyfter man blicken lite brukar det hjälpa. Detsamma kan man förresten säga ang. att optimera på förhand. Se bara vad som skrivs i var och varannan tråd. :)


Alla tider är GMT +2. Klockan är nu 21:51.

Programvara från: vBulletin® Version 3.8.2
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Svensk översättning av: Anders Pettersson