Marc Reichelt: Abfragen über Tabellengrenzen hinaus

Hallo an alle,

ich habe zwei Tabellen, eine namens 'artists' und eine namens 'songs' (hier werden jetzt mal nur die für mein Problem relevanten Spalten dargestellt).
Die Daten dienen nur als Beispiel.

  1. Tabelle 'artists':

id | name            | url
------------------------------------------------
0  | Die Toten Hosen | http://dietotenhosen.de/
1  | Die Ärzte       | http://bademeister.com/

  1. Tabelle 'songs':

id | name      | url                                   | artist_id
-------------------------------------------------------------------
0  | Was Zählt | http://dietotenhosen.de/waszaehlt.mp3 | 0
1  | Der Graf  | http://bademeister.com/dergraf.ogg    | 1
2  | Fliegen   | http://dietotenhosen.de/fliegen.mp3   | 0

Nun habe ich es bisher so gemacht, dass ich eine SELECT-Abfrage auf die zweite Tabelle durchgeführt habe, um daraus dann die Spalte 'artist_id' zu ermitteln und damit dann mit PHP eine weitere Abfrage auf die erste Tabelle gemacht habe, um dort den Namen und die URL des Interpreten festzustellen (songs.artist_id entspricht also artists.id).
Mittlerweile denke ich, dass dies total uneffektiv ist - und zudem ein Problem darstellt, wenn ich nach dem Namen des Interpreten (artists.name) sortieren möchte.

Ich glaube hier des öfteren eine Lösung für dieses Problem gelesen zu haben, doch sogar das Lesen des MySQL-Handbuchs bringt mir nichts. Ich weiß einfach nicht genau, wonach ich Ausschau halten soll - könnte mir jemand auf einfache Art und Weise erklären, wie ich so etwas wie oben praktizieren kann? :-)

Am Besten wäre, wenn ich die Ergebnisse nach einfachen Namen aufrufen könnte, also genauer 'artistname' für den Namen und 'artisturl' für die Homepage-Adresse des Interpreten, 'songname' für den Namen des Songs usw.
Das macht man AFAIK mit dem Schlüsselwort "AS", aber bei solchen Abfragen?

Freundliche Grüße

Marc Reichelt || http://www.marcreichelt.de/

--
Linux is like a wigwam - no windows, no gates and an Apache inside!
Selfcode: ie:{ fl:| br:> va:} ls:< fo:} rl:( n4:( ss:) de:> js:| ch:? sh:| mo:) zu:)
http://emmanuel.dammerer.at/selfcode.html
  1. yo,

    Nun habe ich es bisher so gemacht, dass ich eine SELECT-Abfrage auf die zweite Tabelle durchgeführt habe, um daraus dann die Spalte 'artist_id' zu ermitteln

    um die spalte artist_id zu ermitteln ist die erste tabelle viel geeigneter als die zweite. schließlich handelt es sich in der zweiten tabele "nur" um einen fremdschlüssel, der durchaus auch mehrfach vorkommen kann.

    und damit dann mit PHP eine weitere Abfrage auf die erste Tabelle gemacht habe, um dort den Namen und die URL des Interpreten festzustellen (songs.artist_id entspricht also artists.id).

    auch diese zweite abfrage erübrigt sich, wenn du die erste tabelle nimmst.

    Ich glaube hier des öfteren eine Lösung für dieses Problem gelesen zu haben, doch sogar das Lesen des MySQL-Handbuchs bringt mir nichts. Ich weiß einfach nicht genau, wonach ich Ausschau halten soll

    um ehrlich zu sein, dein problem scheine ich noch nicht ganz verstanden zu haben, da es mir zu triavial erscheint, als dass es eine lösung wäre. aber bis jetzt habe ich noch nichts anderes gelesen, als eine abfrage über die erste tabelle zu machen.

    Am Besten wäre, wenn ich die Ergebnisse nach einfachen Namen aufrufen könnte, also genauer 'artistname' für den Namen und 'artisturl' für die Homepage-Adresse des Interpreten, 'songname' für den Namen des Songs usw.

    ein beispiel:

    SELECT a.name, a.url, s.name, s.url
    FROM Artists AS a, songs AS s
    WHERE a.id = s.artist_id
    AND a.name = $name

    Ilja

  2. Hi,

    was ist jetzt eigentlich deine Frage? (Was genau willst du darstellen?)

    MySQL kann doch Left/Right Joins, oder? Genauso wie Ilja erscheint mir
    die Lösung deines Problems mittels eines Joins zu banal, zu einfach.

    Gruß, Frank

    1. Hallo Frank,

      was ist jetzt eigentlich deine Frage? (Was genau willst du darstellen?)

      MySQL kann doch Left/Right Joins, oder? Genauso wie Ilja erscheint mir
      die Lösung deines Problems mittels eines Joins zu banal, zu einfach.

      OK, ich habe mich vielleicht nicht gut genug ausgedrückt. Ich möchte am Ende ein Resultat wie dieses hier haben:

      song_id | songname  | songurl                               | artist_id | artistname      | artisturl
      ---------------------------------------------------------------------------------------------------------------------
      0       | Was Zählt | http://dietotenhosen.de/waszaehlt.mp3 | 0         | Die Toten Hosen | http://dietotenhosen.de/
      1       | Der Graf  | http://bademeister.com/dergraf.ogg    | 1         | Die Ärzte       | http://bademeister.com/
      2       | Fliegen   | http://dietotenhosen.de/fliegen.mp3   | 0         | Die Toten Hosen | http://dietotenhosen.de/

      Die SQL-Abfrage für das Ding ist an sich ja auch nicht so schwer, bis auf mein kleines Problem.
      Erst mal die SQL-Abfrage:

      SELECT songs.id AS song_id, songs.name AS songname, songs.url AS songurl, songs.artist_id AS artist_id, (...) AS artistname, (...) AS artisturl ORDER BY artistname LIMIT 49,99;

      An diesem Konstrukt wird wohl eher deutlich was ich erreichen möchte. Ich weiß nur nicht, was ich bei den Punkten einsetzen soll - der artistname und die artisturl sollen in Abhängigkeit von artist_id ermittelt werden.
      Als Beispiel lasse ich jetzt mal nach dem artistname sortieren, das Ganze soll natürlich beliebig nach anderen Spaltennamen (bsp. nach der Zeit des Eintrags oder nach dem Songnamen) sortiert werden können.

      Hoffentlich gibt es für dieses Problem eine einfache Lösung... :-)
      Vielleicht hast du Recht und JOIN ist das was ich brauche, aber aus der Dokumentation werde ich mangels guter Erklärung einfach nicht schlau. :-(
      Was macht JOIN denn genau?

      Grüße

      Marc Reichelt || http://www.marcreichelt.de/

      --
      Linux is like a wigwam - no windows, no gates and an Apache inside!
      Selfcode: ie:{ fl:| br:> va:} ls:< fo:} rl:( n4:( ss:) de:> js:| ch:? sh:| mo:) zu:)
      http://emmanuel.dammerer.at/selfcode.html
      1. Hallo Marc,

        MySQL kann doch Left/Right Joins, oder? Genauso wie Ilja erscheint mir
        die Lösung deines Problems mittels eines Joins zu banal, zu einfach.

        Mir auch, außer dass Du einen JOIN benötigst :-)

        OK, ich habe mich vielleicht nicht gut genug ausgedrückt. Ich möchte am Ende ein Resultat wie dieses hier haben:

        song_id | songname  | songurl                               | artist_id | artistname      | artisturl

        0       | Was Zählt | http://dietotenhosen.de/waszaehlt.mp3 | 0         | Die Toten Hosen | http://dietotenhosen.de/
        1       | Der Graf  | http://bademeister.com/dergraf.ogg    | 1         | Die Ärzte       | http://bademeister.com/
        2       | Fliegen   | http://dietotenhosen.de/fliegen.mp3   | 0         | Die Toten Hosen | http://dietotenhosen.de/

        Dazu benötigst Du einen INNER JOIN :-)

        Die SQL-Abfrage für das Ding ist an sich ja auch nicht so schwer, bis auf mein kleines Problem.
        Erst mal die SQL-Abfrage:

        SELECT songs.id AS song_id, songs.name AS songname, songs.url AS songurl, songs.artist_id AS artist_id, (...) AS artistname, (...) AS artisturl ORDER BY artistname LIMIT 49,99;

        1. Lass einmal LIMIT aussen vor :-)
           Das kommt später

        2. Überschriften umschreiben ist ebenfalls für den Grundansatz überflüssig.
           Das kommt später

        3. Was Du derzeit hast, ist ein CROSS JOIN, das Kreuzprodukt der beiden
           Mengen.

        An diesem Konstrukt wird wohl eher deutlich was ich erreichen möchte. Ich weiß nur nicht, was ich bei den Punkten einsetzen soll - der artistname und die artisturl sollen in Abhängigkeit von artist_id ermittelt werden.

        Aus Deinem SQL-Code auf keinen Fall. Das ist nicht das, was Du willst!
        Du willst den INNER JOIN.

        Als Beispiel lasse ich jetzt mal nach dem artistname sortieren, das Ganze soll natürlich beliebig nach anderen Spaltennamen (bsp. nach der Zeit des Eintrags oder nach dem Songnamen) sortiert werden können.

        Kein Problem, die Lösung heißt (INNER) JOIN.

        Hoffentlich gibt es für dieses Problem eine einfache Lösung... :-)

        Ja, die heißt INNER JOIN.

        Vielleicht hast du Recht und JOIN ist das was ich brauche, aber aus der Dokumentation werde ich mangels guter Erklärung einfach nicht schlau. :-(

        Was macht JOIN denn genau?

        Ein Join ist eine Verknüpfung zweier Tabellen. Das Ergebnis der Verknüpfung ist eine weitere Tabelle:

        Join: A × B -> C

        Ganz einfach:

        Aus der Join-Bedingung leitet sich die Art des Joins ab:

        Wenn Du keine JOIN-Bedingung angibst, dann erhälst Du als Ergebnismenge das Kreuzprodukt der beiden Ausgangsmengen (auch CROSS JOIN genannt und selten gewünscht.

        Tabelle A   Tabelle B

        id | Wert    id | Wert
        ---------    ---------
        1  | A       1  | Marc
        2  | B       2  | Ilja
        3  | C       2  | Frank
        4  | D

        Den Cross-Join erhältst Du mit

        SELECT A.id, A.Wert, B.id, B.Wert FROM A, B

        A.id A.Wert B.id B.wert
        -----------------------
        1    A      1    Marc
        1    A      2    Ilja
        1    A      2    Frank
        2    B      1    Marc
        2    B      2    Ilja
        2    B      2    Frank
        3    C      1    Marc
        3    C      2    Ilja
        3    C      2    Frank
        4    D      1    Marc
        4    D      2    Ilja
        4    D      2    Frank

        Dies ist in den seltensten Fällen gewünscht. Die Anzahl der Zeilen ist Anzahl Zeilen in A mal Anzahl Zeilen in B. Die Abfrage kann eventuell _sehr_ viel Zeit in Anspruch nehmen :-)

        Möchtest Du nur die Datensätze, bei denen die Werte in den id-Feldern übereinstimmen, so erhältst Du den NATURAL JOIN (normalerweise alle Spalten beider Tabellen). Dies kannst Du je nach SQL-Dialekt über verschiedene Syntax erreichen:

          
        SELECT  
            A.id,  
            A.Wert,  
            B.id,  
            B.Wert  
        FROM A  
        INNER JOIN B  
        ON A.id = B.id /* Join-Bedingung */  
        
        

        oder

          
        SELECT  
            A.id,  
            A.Wert,  
            B.id,  
            B.Wert  
        FROM A  
        INNER JOIN B USING (id) /* Join-Bedingung */  
        
        

        oder einfach als WHERE-Klausel (was mir persönlich widerstrebt)

          
        SELECT  
            A.id,  
            A.Wert,  
            B.id,  
            B.Wert  
        FROM A, B  
        WHERE A.id = B.id  
        
        

        Ergebnis:
        A.id A.Wert B.id B.wert
        -----------------------
        1    A      1    Marc
        2    B      2    Ilja
        2    B      2    Frank

        Soll nun die Spalte der JOIN-Bedingung nur einmal angezeigt werden, so haben wir den INNER JOIN, Beispiel:

          
        SELECT  
            A.id,  
            A.Wert,  
            B.Wert  
        FROM A  
        INNER JOIN B  
        ON A.id = B.id /* Join-Bedingung */  
        
        

        Ergebnis:
        A.id A.Wert B.wert
        -----------------------
        1    A      Marc
        2    B      Ilja
        2    B      Frank

        Ob Du in diesem Fall die Spalte aus A oder B nimmst, spielt keine Rolle.

        Theta-Join:
        Ist die Bedingung keine Gleichheitsbeziehung, sondern z.B. eine "Kleiner als" oder "Größer als"-Relation, so spricht man von einem Theta-Join.

        LEFT (OUTER) JOIN:
        Möchtest Du _alle_ Datensätze aus A, auch wenn in B kein passender Detaildatensatz vorhanden ist, so verwendest Du den LEFT OUTER JOIN:
        In den entsprechenden Spalten, die aus Tabelle B stammen, steht dann in der Ergebnistabelle der NULL-Wert (nicht die Zeichenfolge "NULL", nicht die Zahl 0).

          
        SELECT  
            A.id,  
            A.Wert,  
            B.id,  
            B.Wert  
        FROM A  
        LEFT OUTER JOIN B  
        ON A.id = B.id /* Join-Bedingung */  
        
        

        Ergebnis:
        Ergebnis:
        A.id A.Wert B.id  B.wert
        -----------------------
        1    A      1     Marc
        2    B      2     Ilja
        2    B      2     Frank
        3    C      NULL  NULL
        4    D      NULL  NULL

        Zu den Join-Spalten: Diese müssen _nicht_ den gleichen Namen tragen (außer Du willst USING verwenden), viele DBMS verlangen jedoch, dass sie von gleichem Datentyp und gleicher Grösse sind. Dazu konsultiere das Handbuch Deines DBMS.

        Freundliche Grüße

        Vinzenz

        1. Hallo Vinzenz,

          vielen Dank für deine sehr ausführliche und super verständliche Erläuterung, ich bin begeistert!
          Was meintest du mit:

          1. Lass einmal LIMIT aussen vor :-)
               Das kommt später

          Kann das bei einem JOIN eventuell Probleme machen, und wenn ja, wie kann ich das gewünschte dann erreichen?

          1. Überschriften umschreiben ist ebenfalls für den Grundansatz überflüssig.
               Das kommt später

          Klar, ich kann ja ebensogut artists.name oder artists.url verwenden, die durchaus auch für sich sprechend sind. :-)

          Viele freundliche Grüße von einem nun richtig glücklichen

          Marc Reichelt || http://www.marcreichelt.de/

          --
          Linux is like a wigwam - no windows, no gates and an Apache inside!
          Selfcode: ie:{ fl:| br:> va:} ls:< fo:} rl:( n4:( ss:) de:> js:| ch:? sh:| mo:) zu:)
          http://emmanuel.dammerer.at/selfcode.html
          1. yo,

            1. Lass einmal LIMIT aussen vor :-)
                 Das kommt später

            Kann das bei einem JOIN eventuell Probleme machen, und wenn ja, wie kann ich das gewünschte dann erreichen?

            nein, es macht grundsätzlich keine probleme, wenn man verstanden hat, wie die LIMIT klausel unter mysql funktioniert. aber erst einmal gilt kis (keep it simple), sprich eins nach dem anderen. wenn du deinen INNER JOIN erfolreich ausgeführt hast, kannst du dann weiter experiementiern und LIMIT hinzufügen.

            btw. ein LEFT JOIN würde auch gehen, für den fall dass du Interpreten hast, die noch keine Lieder in der datenbank eingetragen haben. aber das verwirrt vielleicht weider zuviel.

            Ilja

          2. echo $begrüßung;

            1. Lass einmal LIMIT aussen vor :-)
                 Das kommt später

            Kann das bei einem JOIN eventuell Probleme machen, und wenn ja, wie kann ich das gewünschte dann erreichen?

            Wenn ich richtig vermute, spielt Vinzenz auch auf die Art an, wie die Klauseln in SQL-Befehlen im allgemeinen abgearbeitet werden. [1] In der Reihenfolge wie sie notiert werden müssen [2],

            SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT ...

            so werden sie auch auf die Datenmenge angewendet. [3]

            Mit FROM weiß die DB welche Tabellen verwendet werden sollen. [4]
            Mit WHERE werden die Ergebnisdaten eingeschränkt.
            Mit GROUP BY werden diese Daten zwecks Berechnung von z.B. statistischen Werten zusammengefasst.
            Mit HAVING werden weitere Einschränkungen dieser gruppierten und berechneten Daten vorgenommen.
            ORDER BY sortiert das Ergebnis.
            und LIMIT schränkt nochmal die Anzahl der Datensätze anhand ihrer jetzigen Position ein.

            Beispiel gefällig?

            SELECT LEFT(name,1) AS firstletter,COUNT(*) AS quantity
            FROM users
            WHERE NOT role='admin'
            GROUP BY firstletter
            HAVING quantity>1
            ORDER BY firstletter
            LIMIT 10,5   [5]

            Von den users werden alle normal Sterblichen (ungleich admin) verwendet. Diese werden anhand ihrer Anfangsbuchstaben/-zeichen zusammengefasst und gezählt. Ins Ergebnis kommen aber nur die Anfangsbuchstaben, die mehr als 1x vertreten sind. Vom sortierten Ergebnis werden aber nur 5 Datensätze ab Position 10 zurückgegeben.

            echo "$verabschiedung $name";

            [1] Du scheinst noch nicht allzuviel über SQL zu wissen. Ich denke, dass die folgende Erläuterung da sicher nicht schaden wird.

            [2] natürlich müssen nicht immer alle Klauseln angegeben werden, aber wenn, dann nur so.

            [3] Dazu habe ich leider nicht gefunden, ob/wo das in der MySQL-Doku erklärt ist. Ich hoffe, meine (Ein)bildung stimmt. Wenn nicht, bitte korrigieren oder eine Dokumentation verlinken. Danke.

            [4] JOINs gehören zum FROM und sind deshalb nicht extra erwähnt.

            [5] "Don't try this at home, kids." :-) Nicht in allen Klauseln ist die Verwendung der unter SELECT definierten Aliasse (AS ...) erlaubt. Ich fügte sie der besseren Lesbarkeit wegen trotzdem ein.