• Jetzt anmelden. Es dauert nur 2 Minuten und ist kostenlos!

Datenbankabfrage, If und Else

UweB

Neues Mitglied
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
 
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']))
           . '<br />';
        $i++;
    }
} else {
    echo 'Keine Einträge für Spiel ' . htmlspecialchars($title);
}
 
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:

Code:
    <!-- 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:

Code:
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
Code:
<?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 <!-- Showcase Highscoreliste / Begin -->...<!-- Showcase Highscoreliste / End --> 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:

Code:
      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";
 
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:

Code:
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:

Code:
[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
 
Zuletzt bearbeitet:
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)

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:

Code:
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:

Code:
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";
 
Zuletzt bearbeitet:
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
 
Stimmt, wurde durch deinen Code anscheinend behoben, ich hatte das garnicht erneut überprüft. Bei dieser Abfrage kamen die User mehrfach vor:

Code:
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:

Code:
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:

Code:
$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);
 
So klappt es leider nicht, aber ich versuche da wohl besser mal im Drupal-Forum weiter zu kommen. Danke für deine Hilfe!
 
Zurück
Oben