Datenbankabfrage, If und Else

Hallo,
in meiner Datenbank gibt es eine Tabelle „highscores“ mit den Feldern „game“, „username“, „score“ etc., wobei „game“ der Titel des Spiels ist, der über die Variable $title bei Übermittlung der Punkte eines Spielers mit an die Datenbank übergeben wird.

Da manche Spiele keine Highscore-Funktion haben würde ich nun gerne eine Abfrage starten, ob das Spiel $title im Feld „game“ in der Tabelle „highscore“ vorhanden ist. Wenn ja, sollen die ersten zehn (orientiert an den Score-Werten, absteigend) „username“ etc. angezeigt werden, andernfalls soll nichts ausgegeben werden.

Wie kann ich das hinbekommen?

Gruß,
Uwe

Ich gehe davon aus das du MySql verwendest.

Füge zum Query WHERE game IS NOT NULL hinzu, dadurch werden nur Datensätze ausgelesen die nicht null sind.

MySQL :: MySQL 5.1 Referenzhandbuch :: 3.3.4.6 Mit NULL-Werten arbeiten

[php]<?php

// Sicherstellen, dass Seite als UTF-8 ausgeben wird
header(‚Content-Type: text/html; charset=UTF-8‘);

mysql_connect(‚localhost‘, ‚username‘, ‚password‘);
mysql_query(„SET NAMES ‚utf8‘“);
mysql_select_db(‚dbname‘);

$title = ‚Skat‘;

$q = „SELECT username, score
FROM highscores
WHERE game = '“ . mysql_real_escape_string($title) . „’
ORDER BY score DESC
LIMIT 0,10“;

$res = mysql_query($q);

if (mysql_num_rows($res) > 0) {
$i = 1;
while ($row = mysql_fetch_assoc($res)) {
echo htmlspecialchars(sprintf(‚Rang %s: %s - %s Punkte‘,
$i, $row[‚username‘], $row[‚score‘]))
. ‚
‘;
$i++;
}
} else {
echo 'Keine Einträge für Spiel ’ . htmlspecialchars($title);
}[/php]

Erstmal danke für die Antworten, leider funktioniert das so noch nicht.

Falls sich jemand mit Drupal auskennt, die Spiele sind Nodes und werden über die Datei node-onlinespiel.tpl.php ausgegeben. Die sieht im relevanten Bereich folgendermaßen aus:

<!-- Showcase Highscoreliste / Begin --> <div id="text"> <div class="css_head"><h2><?php print $title ?> - Highscoreliste</h2></div> <div class="css_body"> <div class="infos"> <ol> <li><a href="/user/username" title="username">username</a> erreichte 12345678910 Punkte am 01.01.2010</li> <li><a href="/user/username" title="username">username</a> erreichte 12345678910 Punkte am 01.01.2010</li> <li><a href="/user/username" title="username">username</a> erreichte 12345678910 Punkte am 01.01.2010</li> <li><a href="/user/username" title="username">username</a> erreichte 12345678910 Punkte am 01.01.2010</li> <li><a href="/user/username" title="username">username</a> erreichte 12345678910 Punkte am 01.01.2010</li> <li><a href="/user/username" title="username">username</a> erreichte 12345678910 Punkte am 01.01.2010</li> <li><a href="/user/username" title="username">username</a> erreichte 12345678910 Punkte am 01.01.2010</li> <li><a href="/user/username" title="username">username</a> erreichte 12345678910 Punkte am 01.01.2010</li> <li><a href="/user/username" title="username">username</a> erreichte 12345678910 Punkte am 01.01.2010</li> <li><a href="/user/username" title="username">username</a> erreichte 12345678910 Punkte am 01.01.2010</li> </ol> <p>&raquo; <a href="/community/highscores" title="Highscores">Infos zur Highscoreliste</a></p> </div> </div> <div class="css_foot"><img src="/sites/all/themes/my_theme/images/bg_showcase_foot.png"></div> </div> <!-- Showcase Highscoreliste / End --> Über die Variable $title wird der Spieletitel ausgegeben. Um das Spiel eindeutiger zu identifizieren habe ich mich jetzt dazu entschlossen auch noch die Variable $nid (die ID des Nodes, also die eindeutige ID/Zahl passend zum jeweiligen Spiel) an die Datenbank weiterzugeben, über die ich das Spiel dann zuverlässig bei der Abfrage identifizieren möchte.

Die Tabelle highscores habe ich wie folgt erstellt:

CREATE TABLE IF NOT EXISTS `highscores` ( `id` int(11) NOT NULL AUTO_INCREMENT, `when` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `nid` text COLLATE utf8_unicode_ci NOT NULL, `game` text COLLATE utf8_unicode_ci NOT NULL, `gameid` text COLLATE utf8_unicode_ci NOT NULL, `boardid` text COLLATE utf8_unicode_ci NOT NULL, `userid` text COLLATE utf8_unicode_ci NOT NULL, `username` text COLLATE utf8_unicode_ci NOT NULL, `score` text COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;Wobei „gameid“ und „boardid“ nicht ausgegeben werden sollen und auch nicht weiter relevant sind, dort werden lediglich die IDs des Spieleanbieters festgehalten. Ich markiere die Spiele ja nun wie gesagt über „nid“, die Node-ID ($nid) des Drupal-CMS. Das Feld „id“ dient der durchlaufenden Numerierung der Highscores.

Die Daten aus „username“, „score“ und „when“ sollen in der Liste stehen, wenn diese ausgegeben wird, vom höchsten Score ausgehend absteigend, die ersten zehn, jedoch nur von verschiedenen "userid"´s. Das ist sozusagen das Ziel.

Die ID des aktuellen Spiels, also die Variable $nid, kann man über <?php print $node->nid ?> in der template-Datei node-onlinespiel.tpl.php ausgeben. Nach dieser Zahl soll also in der Tabelle „highscore“ im Feld „nid“ gesucht werden.

Wenn keine Highscores vorhanden sind, die „nid“ also nicht in der Tabelle auftaucht, soll der gesamte Bereich … ausgeblendet, bzw. nicht angezeigt werden. Ab einem gefundenen Eintrag soll der Schaukasten in obiger Form ausgegeben werden.

Leider habe ich keine Ahnung von Datenbankabfragen und bin auch nur im Trial-And-Error-Verfahren bis zu diesem Punkt gelangt. Es wäre also wirklich super, wenn mir jemand kurz erklären könnte, wie ich das hinbekomme.

Gruß,
Uwe

Hallo nochmal,
ich muss mich korrigieren - @mermshaus deine Abfrage funktioniert im Prinzip doch, ich hatte nur einen kleinen Übetragungsfehler eingebaut.

Ich habe nun die Variable $title aus deinem Code entfernt und die Sortierung so abgeändert:

      WHERE    `nid` = '" . mysql_real_escape_string($node->nid) . "'

Nun wird das aktuelle Spiel angewählt und die ersten 10 Plätze werden ausgegeben. Allerdings sind die alle vom selben User belegt, wie kann ich das verhindern?

Gruß,
Uwe

Müsste so gehen:

[php]$q = „SELECT username, MAX(score) AS score
FROM highscores
WHERE game = '“ . mysql_real_escape_string($title) . „’
GROUP BY username
ORDER BY score DESC
LIMIT 0,10“;[/php]

Ne, da bekomme ich keine Einträge als Output und es gibt noch ein Problem: Bei der derzeitigen Sortierung sind 66 Punkte mehr als 615 Punkte, wie kann ich das korrigieren?

Gruß,
Uwe

Indem du die Spalte ‚score‘ auf einen numerischen Datentyp setzt und nicht auf ‚text‘. Dein Schema ist insgesamt seltsam. Zwei Spalten ‚userid‘ und ‚username‘ deuten auf fehlende Normalisierung hin. Gleiches gilt für ‚game‘ und ‚gameid‘. Die id-Spalten sollten alle numerisch sein, ‚when‘ sollte vermutlich vom Typ DATETIME sein. Usw.

Leider kenne ich kein gutes Tutorial, aber lies ruhig mal eins.

Die Datenbankstruktur habe ich aus einem anderen Forum, die wurde speziell für die Highscore-Tabelle veröffentlicht. Was meinst du mit fehlender Normalisierung? Eine typische Zeile in der Tabelle sieht so aus:

1     2010-02-09 12:08:24     112     Stomp That Alien     fe55f6c8eb27201f     e1cade7fcd20bab8319ba233fa92754c     10     DrGonzo     155

Die Felder gameid und boardid dienen der Identifizierung beim Spieleanbieter, userid und username habe ich analog zu den Daten in der restlichen (Drupal-)Datenbank eingerichtet.

Ich kann die Tabelle auch löschen und neu erstellen, befindet sich derzeit eh noch alles in einer Testumgebung. Wie müsste der Create Table Code dann aussehen, damit die Sortierung funktioniert?

Gruß,
Uwe

Ich habe mal in den anderen Tabellen meiner Drupal-Installation rumgeschaut und die Higchscore-Tabelle nun auf folgende Einstellungen geändert:

[U] id[/U] int(11) Nein auto_increment [U]when[/U] timestamp Nein CURRENT_TIMESTAMP [U]nid[/U] int(10) UNSIGNED Nein [U]game[/U] varchar(60) utf8_general_ci Nein [U]gameid[/U] varchar(16) utf8_general_ci Nein [U]boardid[/U] varchar(32) utf8_general_ci Nein [U]userid[/U] int(10) UNSIGNED Nein [U]username[/U] varchar(60) utf8_general_ci Nein [U]score[/U] float Nein

Also die Auflistung der Punktestände ist nun korrekt. Ich denke mal, dass ergibt so dann wohl auch mehr Sinn, richtig?

Edit: Der Codeschnipsel MAX(score) AS score funktioniert allerdings immer noch nicht und führt zu keinem Ergebnis.

Gruß,
Uwe

Das haut schon hin.

[code]mysql> SHOW CREATE TABLE highscores\G
*************************** 1. row ***************************
Table: highscores
Create Table: CREATE TABLE highscores (
id int(11) NOT NULL AUTO_INCREMENT,
when timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
nid int(10) unsigned NOT NULL,
game varchar(60) NOT NULL,
gameid varchar(16) NOT NULL,
boardid varchar(32) NOT NULL,
userid int(10) unsigned NOT NULL,
username varchar(60) NOT NULL,
score float NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> SELECT username, MAX(score) AS score
→ FROM highscores
→ WHERE game = ‚Skat‘
→ GROUP BY username
→ ORDER BY score DESC
→ LIMIT 0,10;
±---------±------+
| username | score |
±---------±------+
| Marc | 72 |
| Peter | 63 |
±---------±------+
2 rows in set (0.00 sec)[/code]

Ja, so ergibt das Schema mehr Sinn. Den „username“ solltest du trotzdem nicht zusätzlich speichern müssen. Der dürfte doch in einer Tabelle „users“ vorliegen und anhand der ID zu ermitteln sein, oder? Was du da machst, ist redundant, weil du dieselbe Information (den Namen) oftmals ablegst.

Aber gut, um das eindeutig sagen zu können, bräuchte man mehr Informationen über den Gesamtaufbau. Verweisen die IDs auf Daten, die in der eigenen DB vorgehalten werden?

Das stimmt, der Username ist redundant, ich dachte nur so wäre es für mich einfacher. Aber besser ist es wohl, wenn ich diesen weglasse und ihn über die UserID ausgebe.

Die Abfrage sieht nun übrigens so aus:

SELECT h.username, h.score, h.time FROM highscores h, (SELECT username, MAX(score) AS max_score FROM highscores WHERE nid = {$nid} GROUP BY username) AS mpu WHERE h.username = mpu.username AND h.score = mpu.max_score ORDER BY score DESC LIMIT 10

Das funktioniert einwandfrei, aber ich werde dann mal versuchen username durch userid zu ersetzen und den Namen dann über die Drupal-Tabelle user einzufügen. Dann kann ich das Feld username wohl tatsächlich aus der highscores-Tablle entfernen.

Gruß,
Uwe

Hat geklappt, hier ist die Lösung:

SELECT h.userid, h.score, h.time, u.name FROM highscores h, users u, (SELECT userid, MAX(score) AS max_score FROM highscores WHERE nid = {$nid} GROUP BY userid) AS mpu WHERE h.userid = mpu.userid AND h.score = mpu.max_score AND u.uid = h.userid ORDER BY score DESC LIMIT 10

Gruß,
Uwe

Noch 'ne Spur schicker ist es mit einem JOIN. Subselects sind nie gut und „WHERE h.userid = mpu.userid“ (Theta-Stil JOIN) ist nicht die empfohlene Syntax für sowas.

[php]$q = „SELECT h.userid, MAX(h.score) AS score, h.time, u.name
FROM highscores h
LEFT JOIN users u
ON h.userid = u.uid
WHERE h.nid = " . (int) $nid . "
GROUP BY h.userid
ORDER BY score DESC
LIMIT 0,10“;[/php]

Danke für den Tipp, ich habe es gleich mal so übernommen und es funktioniert ebenfalls. Nun habe ich noch ein kleines Problem, dass mit erst gestern zufällig aufgefallen ist:

Wenn ein Spieler eine identische Punktzahl mehrfach erreicht hat und diese seine Maximalpunktzahl ist, wird er mehrfach in der Liste angezeigt. Wie kann ich erreichen, dass bei Punktgleichheit nur der Datensatz mit dem älteren Timestamp (Feld time in Tabelle highscores) angezeigt wird?

Gruß,
Uwe

Das kann eigentlich nicht sein, wenn du nach userid gruppierst. Zeig mal die Query.

Stimmt, wurde durch deinen Code anscheinend behoben, ich hatte das garnicht erneut überprüft. Bei dieser Abfrage kamen die User mehrfach vor:

SELECT h.userid, h.score, h.time, u.name FROM highscores h, users u, (SELECT userid, MAX(score) AS max_score FROM highscores WHERE nid = {$nid} GROUP BY userid) AS mpu WHERE h.userid = mpu.userid AND h.score = mpu.max_score AND u.uid = h.userid ORDER BY score DESC LIMIT 10

Dann bleibt eigentlich nur noch eine letzte Frage:

Die Abfrage befindet sich in einer Drupal-Installation in der Template-Datei node-onlinespiel.tpl.php - momentan stelle ich die Verbindung zur Datenbank wie folgt her:

mysql_connect('localhost', 'db', 'pw');
mysql_select_db('dbname');

$q = "...

Ich habe bereits im Drupal-Forum nachgefragt, aber leider keine gescheite Antwort darauf bekommen, wie ich direkt auf die Drupal-DB-Verbindung zurückgreifen kann. Mir wurde gesagt es geht über die Funktion db_query, aber wie und wo ich diese einfügen soll weiß ich nicht. Ich habe schon ein wenig rumprobiert, aber es kommt nichts bei raus und ich finde auch keine passende Anleitung für mein Anliegen die ich verstehen würde.

Die Zugangsdaten zur Datenbank liegen in /sites/defaulft/settings.php in dieser Form vor:

$db_url = 'mysqli://db:pw@localhost/dbname';
$db_prefix = '';

Wie kann ich darauf zurückgreifen?

Gruß,
Uwe

Ja, db_query klingt nicht schlecht. In etwa so könnte das laut Doku hinhauen (genau weiß ich’s aber nicht):

[php]$result = db_query(„SELECT h.userid, MAX(h.score) AS score,
h.time, u.name
FROM highscores h
LEFT JOIN users u
ON h.userid = u.uid
WHERE h.nid = %d
GROUP BY h.userid
ORDER BY score DESC
LIMIT 0,10“, $nid);[/php]

So klappt es leider nicht, aber ich versuche da wohl besser mal im Drupal-Forum weiter zu kommen. Danke für deine Hilfe!