Schleifen-Problem

Maniac_81
Ich habe mir einen Hack gebaut, der die Userbeiträge bearbeitet. Nun macht es aber nicht das was ich genau möchte, scheint ein problem in den Schleifen zu sein:

Das ist der Auszug aus der acp/otherthuff.php

php:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
list($minzeichen) = $db->query_first("SELECT wert FROM bb".$n."_minzeichen WHERE id=1");
            
            $result $db->query("SELECT userid FROM bb".$n."_users ORDER BY userid ASC"$perpage$perpage * ($page 1));
            while ($row $db->fetch_array($result)) {
                $db->query_first("UPDATE bb".$n."_users SET userposts=0 WHERE userid=".$row['userid']);
                
                $result $db->unbuffered_query("SELECT message FROM bb".$n."_posts WHERE userid=".$row['userid']);
                
                while($data $db->fetch_array($result)) {
                    if (wbb_strlen($data['message']) >= $minzeichen)
                        {
                            $db->unbuffered_query("UPDATE bb".$n."_users SET userposts = userposts + 1 WHERE userid = ".$row['userid']);                        
                        }
                }                
            }
            refresh("otherstuff.php?sid=$session[hash]&action=userposts&perpage=$perpage&page=".($page 1), $lang->items['LANG_ACP_OTHERSTUFF_USERPOSTS'], round($page $perpage $totalcount 100)."&on_off=1");
        }


Eigentlich sollte die 1. Schleife die userposts auf 0 setzen und die posts auslesen - das funktioniert.
in der 2. Schleife soll dann überprüft werden ob die die posts eine bestimmte länge haben, wenn ja dann userposts +1, das funktioniert auch!

was aber nicht funktioniert, das das mit allen Usern gemacht wird. Es wird nur ein user abgearbeitet, folglich stimmt was mit den schleifen nicht.

Es sind mehrere User angelegt und haben auch verschiedene beitragslängen.

Edit: Fehler gefunden......und dafür hab ich 2 Tage gesucht.....hätte mich lieber mal ausschlafen sollen.....lol
Hawkes
Ääähm tschuldigung, aber das ist grottig. Du sendest SQL Queries in sogar VERSCHACHTELTEN while schleifen? Schlimmer gehts nimmer.
Maniac_81
und welcher weg wäre besser?

ich kann mir nicht vorstellen das mit tabellenverknüpfungen zu lösen....
Hawkes
Ich kenne die Tabellenstruktur des WBB 2 nicht, daher mal ein wenig pseudocodisch:

php:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
list($minzeichen) = $db->query_first("SELECT wert FROM bb".$n."_minzeichen WHERE id=1");

$sql "SELECT user.userid, post.message
                                    FROM             bb".$n."_users user
                                    LEFT JOIN bb".$n."_posts post
                                    ON (user.userid = post.userID)
                                    ORDER BY userid ASC";
/*
 * What's the point in only grabbing the users and post for a certain page-no?
 * Do you want to update them per page? A lot of clicking :P
 */
$result $db->query($sql$perpage$perpage * ($page 1));
$userData = array();
while ($row $db->fetch_array($result)) {
    // init the user record
    if (!isset($userData[$row['userid']])) $userData[$row['userID']] = 0;

    if (wbb_strlen($row['message']) >= $minzeichen) {
        $userData[$row['userID']]++;        
    }
}

$inserts '';
foreach ($userData as $userID => $userposts) {
    if (!empty($inserts)) $inserts .= ',';
    $inserts .= '('.$userID.', '.$userposts.')';    
}

// INSERT INTO ... DUPLICATE KEY should work well or updating all rows here. Just make sure that userid is primary or unique
$sql "INSERT INTO bb".$n."_user
            (userid, userposts)
            VALUES            
            ".$inserts."
        ON DUPLICATE KEY UPDATE userposts = VALUES(userposts)";
$db->query($sql);
refresh("otherstuff.php?sid=$session[hash]&action=userposts&perpage=$perpage&page=".($page 1), $lang->items['LANG_ACP_OTHERSTUFF_USERPOSTS'], round($page $perpage $totalcount 100)."&on_off=1");


Achtung. Das Feld userid in bbx_users muss einen PRIMARY oder UNIQUE Index haben.

Dieser Ansatz verbraucht jetzt lediglich 2 Queries für die Operation. Plus 1 Query für den Optionswert (das ist im WBB 3 vieeeeeeeel besser gelöst).
Maniac_81
funktioniert leider nicht. hab nur 2 stellen auf die struktur der wbb2 DB anpassen müssen.

hier das was ich versucht habe:

php:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
$sql "SELECT user.userid, post.message
                                    FROM             bb".$n."_users user
                                    LEFT JOIN bb".$n."_posts post
                                    ON (user.userid = post.userid)
                                    ORDER BY userid ASC";
/*
 * What's the point in only grabbing the users and post for a certain page-no?
 * Do you want to update them per page? A lot of clicking :P
 */
$result $db->query($sql$perpage$perpage * ($page 1));
$userData = array();
while ($row $db->fetch_array($result)) {
    // init the user record
    if (!isset($userData[$row['userid']])) $userData[$row['userID']] = 0;

    if (wbb_strlen($data['message']) >= $minzeichen) {
        $userData[$row['userID']]++;        
    }
}

$inserts '';
foreach ($userData as $userID => $userposts) {
    if (!empty($inserts)) $inserts .= ',';
    $inserts .= '('.$userID.', '.$userposts.')';    
}

// INSERT INTO ... DUPLICATE KEY should work well or updating all rows here. Just make sure that userid is primary or unique
$sql "INSERT INTO bb".$n."_users
            (userid, userposts)
            VALUES            
            ".$inserts."
        ON DUPLICATE KEY UPDATE userposts = VALUES(userposts)";
$db->query($sql);
refresh("otherstuff.php?sid=$session[hash]&action=userposts&perpage=$perpage&page=".($page 1), $lang->items['LANG_ACP_OTHERSTUFF_USERPOSTS'], round($page $perpage $totalcount 100));


hier kommt folgender Fehler:
Hawkes
Ersetze bei wbb_strlen das $data noch durch $row. Copy&Paste Fehler.

Ansonsten bin ich schon zu sehr an die WCF Codeguidelinesg gewöhnt xD


php:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
list($minzeichen) = $db->query_first("SELECT wert FROM bb".$n."_minzeichen WHERE id=1");

$sql "SELECT user.userid, posts.message
                                    FROM             bb".$n."_users user
                                    LEFT JOIN bb".$n."_posts post
                                    ON (user.userid = post.userID)
                                    ORDER BY userid ASC";
/*
 * What's the point in only grabbing the users and post for a certain page-no?
 * Do you want to update them per page? A lot of clicking :P
 */
$result $db->query($sql$perpage$perpage * ($page 1));
$userData = array();
while ($row $db->fetch_array($result)) {
    // init the user record
    if (!isset($userData[$row['userid']])) $userData[$row['userid']] = 0;

    if (wbb_strlen($row['message']) >= $minzeichen) {
        $userData[$row['userid']]++;        
    }
}

$inserts '';
foreach ($userData as $userID => $userposts) {
    if (!empty($inserts)) $inserts .= ',';
    $inserts .= '('.$userID.', '.$userposts.')';    
}

// INSERT INTO ... DUPLICATE KEY should work well or updating all rows here. Just make sure that userid is primary or unique
$sql "INSERT INTO bb".$n."_user
            (userid, userposts)
            VALUES            
            ".$inserts."
        ON DUPLICATE KEY UPDATE userposts = VALUES(userposts)";
$db->query($sql);
refresh("otherstuff.php?sid=$session[hash]&action=userposts&perpage=$perpage&page=".($page 1), $lang->items['LANG_ACP_OTHERSTUFF_USERPOSTS'], round($page $perpage $totalcount 100)."&on_off=1");


Probiers nochmal smile
Maniac_81
immernoch Fehler:

[attach]31519[/attach]

was mich allerdings etwas verdutzt ist die foreach-schleife. da kapier ich dein vorgehen nicht.

edit habs jetzt mit :

php:
1:
2:
3:
4:
5:
$sql "INSERT INTO bb".$n."_users
            (userid, userposts)
            VALUES            
            ".$inserts."
        ON DUPLICATE KEY UPDATE userposts = VALUES($userposts)";


versucht aber kommt trotzdem der selbe fehler.
Hawkes
Welchen Datentyp hat denn userid im WBB2? Aus irgendeinem Grund scheinen da leere Strings (=NULL?) anstatt einer userID zurückzukommen.

Dafür kenne ich aber das WBB 2 zu wenig.

Das Vorhaben ist generell folgendes:

1. Verwende ein sinnloses Query um alle userIDs und postMessages zubekommen. Das Ganze sollte auch ohne JOIN gehen fällt mir gerade auf. Ich bin nur von deinen vielen Queires verwirrt gewesen großes Grinsen

php:
1:
$sql "SELECT userid, message                                     FROM             bb".$n."_posts     ORDER BY userid ASC";


2. Lege ein Array an, in dem für alle Benutzer der Postcounter gezählt wird.
3. Prüfe ob ein Post lang genug ist und wenn ja erhöhe den Postcounter des users um 1.
4. Baue mit einer for schleife INSERT Blöcke für ein SQL Query. Jeder Insert-Block besteht aus userid, userposts
5. Sende ein INSERT ... ON DUPLICATE KEY UPDATE query auf die usertabelle ab und nutze hierbei aus, dass bei dieser SQL Syntax bestehende Datensätze einfach aktualisiert werden ohne für jeden Datensatz ein extra Query zu verwenden.
Maniac_81
php:
1:
$sql "SELECT userid, message                                     FROM             bb".$n."_posts     ORDER BY userid ASC";


mit dieser abfrage bekomme ich aber doch nicht die messages vom benutzer, sondern es wird aufgelistet:

userid: 1
message: 1

userid: 1
message: 2

userid: 2
message: 3

userid: 2
message: 4

usw...

mit nem join wähle ich doch aus das mit in einer schleife erst die messages von userid 1 überprüft werden usw...

oder denke ich da voll daneben?

Datentyp:

code:
1:
2:
3:
4:
`userid` int(11) unsigned NOT NULL auto_increment,
PRIMARY KEY  (`userid`),
Hawkes
Huh?

Also kp was das WBB 2 macht, aber das erste Query muss ein Resultset liefern, dass die userid und den Inhalt des Posts liefert. Kp wie die Felder im WBB 2 heißen. Wenn der für message 1,2,3,4 usw. zurückliefert, dann ist message wohl nicht das Feld, dass den Text enthält.

Ein JOIN ist einfach eine Tabellenverknüpfung anhand eines Feldes, dass in 2 Tabellen jeweils vorhanden ist.
Maniac_81
message ist natürlich nicht numerisch, das sollte nur symbolisch sein.

ist das mit den insert nun richtig das ich es als variable geschrieben hab? muss das VALUES auch in das UPADTE rein?

weil ich habs auch ohne versucht:

php:
1:
2:
3:
4:
5:
$sql "INSERT INTO bb".$n."_users
            (userid, userposts)
            VALUES            
            ".$inserts."
        ON DUPLICATE KEY UPDATE userposts = $userposts";


trotzdem gleicher fehler....

Edit:

jetzt hab ichs noch ein bissl eingrenzen können und die Fehlermeldung wird schon eindeutiger ausgegeben.

hier war der Fehler:

php:
1:
2:
$userData[$row['userID']]++;
// muss userid heissen
Hawkes
Das Query muss aber wieder zurück. Das ist so falsch. Außerdem schreibt man keine PHP Variablen mehr in die Strings rein. Das zu evaluieren dauert auch bei PHP 5 immer noch länger als ein zusammengesetzer String. Bei PHP 4 hat man sogar 30% Performancegewinn.
Maniac_81
aber nur "userposts" wird doch nicht verarbeitet, oder wie würdest es du schreiben?

laut der fehlermeldung wird ja im ersten durchlauf etwas vergessen:

VALUES(,1),(0,1),(1,2) usw...

wo liegt da der fehler? ich habe es versucht mal diese zeile wegzulassen, da es ja keinen user mit der id 0 gibt:
php:
1:
if (!isset($userData[$row['userid']])) $userData[$row['userid']] = 0;


dabei ist das script bis zum ende gelaufen, aber nun hatte jeder user die gleiche anzahl an posts.
Hawkes
VALUES(userposts) ist eine Anweisung an SQL, dass er bei einem doppelt existierenden Eintrag aus der VALUES Auflistung den userposts Wert zum Updaten verwendet. Kannst dich dazu im MySQL Referenzhandbuch etwas einlesen.

Du hast die weggelassen Zeile etwas falsch verstanden. Noch nicht soviel Erfahrung mit PHP? Das weist lediglich jeder userID einen Anfangswert für die Beitragszahl von 0 zu.

Du musst eher mit var_dumps ( Wenn du eine IDE verwendest kanns du auch richtig debuggen) dir mal das userData array ausgeben lassen und zurückverfolgen, warum der Key enen leeren Wert hat. Anscheinend gibt das SQL Query einen Datensatz zurück, wo userID = NULL ist. Beim ersten Query sollte das eigentlich auftreten. Bei meiner 2ten Version die du jetzt verwendest kann das halt bei Gastpostings verwenden. Du könntest dem SQL Query daher die Bedingung userid <> NULL AND userid <> 0 verwenden. Ich weiß halt nicht genau wie sich das WBB 2 da verhält. Das WBB 3 setzt die userid eines Gastposts auf 0.

Generell ist aber kein Problem, wenn du einen Eintrag mit der userid 0 hast. Nur NULL ist problematisch, da PHP daraus einen leeren String macht, wenn man den NULL Wert in einen String einbettet.

Wenn du mal ein ganzes Paket deines Bastelns schon hast kann ich mir das Ganze auch mal in meinem lokalem Test-WBB2 (Nostalgie großes Grinsen ) angucken.
Maniac_81
Hallo Hawkes,

ja du hast recht ich hab noch nicht so viel erfahrung in PHP wie manch anderer, aber auch ich bin lernfähig :-)

Das "Paket" welche sich m WBB2 ja noch "hacks" nennen, findest du in der datenbank hier. hier ist allerdings noch mit den querys in den schleifen.

Beiträge erst nach minimum Zeichenlänge zählen
Hawkes
Ich hab gleich mal Zeit. Muss mir das Ding nur einbauen (Jaja Paketinstallationen sind was feines Zunge raus )

Nur mal so zur Erklärung: Für mein Forum, dass ein sehr kleines ist (gerade mal 5700 Beiträge und 530 Mitglieder) würde dieses Script etwa 6700 Queries abschicken, wenn es die Neuberechnung für alle Benutzer macht. Dabei ist die Berechnung und Hochzählung eine Sache für PHP und nicht MySQL. Durch die Übermittelungszeiten und alles was so abläuft, wenn man ein Query an einen SQL Server schickt, ist das hier ein Performanceburner schlecht hin und wenn das Ganze nicht portionsweise ausgeführt werden würde, wäre das wohl ideal, um einen SQL Server mit nem DOS in die Knie zu zwingen.

EDIT:

Also... himmelkreuzgranatenhallelujaundachkp seid ihr sicher, dass ihr das WBB 2 angenehmer, intuitiver und benutzerfreundlicher findet? Allein der Hackeinbau war ein Graus und das ACP gibt einem dann den Rest.

Nunja. Wie auch immer: Ich habe in der otherstuff.php deinen Code durch meinen ersetzt und das funktioniert dann auch und verbraucht wie gesagt pro Zyklus nur 2 Queries, was extrem performant und datenbankschonend ist.

php:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
}elseif($on_off == 2){
                
            $result $db->query("SELECT userid, message FROM bb".$n."_posts ORDER BY userid ASC"$perpage$perpage * ($page 1));
            $userData = array();
            while ($row $db->fetch_array($result)) {
                // init the user record
                if (!isset($userData[$row['userid']])) $userData[$row['userid']] = 0;

                if (wbb_strlen($row['message']) >= $minzeichen) {
                    $userData[$row['userid']]++;
                }
            }

            $inserts '';
            foreach ($userData as $userID => $userposts) {
                if (!empty($inserts)) $inserts .= ',';
                $inserts .= '('.$userID.', '.$userposts.')';
            }

            // INSERT INTO ... DUPLICATE KEY should work well or updating all rows here. Just make sure that userid is primary or unique
            $sql "INSERT INTO bb".$n."_user
            (userid, userposts)
            VALUES            
            ".$inserts."
        ON DUPLICATE KEY UPDATE userposts = VALUES(userposts)";
            $db->query($sql);
                
            refresh("otherstuff.php?sid=$session[hash]&action=userposts&perpage=$perpage&page=".($page 1), $lang->items['LANG_ACP_OTHERSTUFF_USERPOSTS'], round($page $perpage $totalcount 100));
        }


Hoffe du kommst so weiter. Was ich dazu sagen muss: Ich habe das natürlich mit PHP 5.2.9 und MySQL 5.1.34 verwendet. Es kann sein, dass sich in PHP 4 irgendwas anders verhält als in PHP 5, was zu dem falschen Eintrag führt. Da müsste ich aber, falls es denn bei dir überhaupt noch Fehler gibt, nochmal nachlesen. Ich habe erst vor 1 1/2 Jahren mit PHP angefangen und da dann natürlich mit PHP 5 und selbst das hat schon genug Macken, da kann ich mich mit all denen von PHP 4 nicht auskennen^^
Maniac_81
Danke das funktioniert nun auf meinem testsystem nach einer kleinen korrektur von "bb".$n."_user - > bb".$n."_users" :-)

Zitat:

Nur mal so zur Erklärung: Für mein Forum, dass ein sehr kleines ist (gerade mal 5700 Beiträge und 530 Mitglieder) würde dieses Script etwa 6700 Queries abschicken, wenn es die Neuberechnung für alle Benutzer macht. Dabei ist die Berechnung und Hochzählung eine Sache für PHP und nicht MySQL.


aber sagt man nicht immer das man berechnungen und dergleichen mysql-erledigen lassen soll? komischerweisse sagt das jeder anders, wobei ich jetzt nichts gegen performanceverbesserung habe :-)
Hawkes
Darüber steht aber der Grundsatz: Lieber einmal lang als mehrfach kurz.

Was deine Variante gemacht hat, war für jeden Beitrag einzeln ein Query an die DB zu schicken und den postCounter um eins zu erhöhen, anstatt einfach nur in PHP eine Variable hoch zu zählen.

Meines erachtens, sollte PRO Query soviel wie Möglich an Vorarbeit durch die Datenbank übernommen werden, also Gruppierungen, Berechnungen und Sortierungen, da hier SQL schneller ist als PHP. Aber in diesem Fall ist diese Art von Berechnung ganz klar ein Fall für PHP. Außerdem haben wir bei der WBB 3 Entwicklung auch den Grundsatz, dass ein Query niemals in einer Schleife zu stehen hat (mit Ausnahmefällen).
Maniac_81
WBB3-Development ist für mich noch zu kompliziert *g* solange programmiere ich noch nicht Zunge raus

das Script funktioniert übrigens auch mit PHP4, wobei ich nicht testen konnte ob es auch mit MySQL < 5 funktioniert.
Hawkes
Joa ich hab mal kurz wegen dieser Art der Verwendung von INSERT... ON DUPLICATE KEY UPDATE gegooglet, aber das scheint MySQL 4.1 auch zu können.

Also klar bei der WBB 3 Entwicklung kommt man mit einfachem PHP Code und Scriptbits, die man mal hier mal da einfügt nichtmehr hin, aber es stehen einem so viel mehr Möglichkeiten zur Verfügung und alles wirkt nochmals deutlich ausgereifter und intuitiver. Ich hab gestern Abend noch ein wenig mit dem ACP des WBB 2 gespielt. Da liegen wirklich welten zwischen WBB 2 und WBB 3. Auch fehlen dem WBB 2 einige administrative Funktionen.