Stoffel: WHERE-Abfrage nach Zahlen (getrennt mit Komma oder Semikolon)

Hallo,
was hab ich heute Abend gesucht - darum sei es mir verziehen, wenn ich mal kurz vor für mich LMAA sagen muss.

Also: Ich habe zwei Tabellen in MySQL:

texte
ID | Text | Quelle
------------------
1  | abc  | 1
------------------
2  | def  | 2
------------------
3  | ghi  | 3
------------------

quellen
QID | Quellenname
-----------------
1   | Amazon
-----------------
2   | Ebay
-----------------
3   | Rebuy
-----------------

Wenn ich die jetzt mit meinem laienhaften Wissen verknüpfe, funktioniert das ganz gut.

  
$sql = "SELECT * FROM texte a, quelle q WHERE a.id = q.qid ORDER BY qid DESC";  

Ich hielt es für angebracht, das über zwei Tabellen zu lösen und bin, wie gesagt, noch im "Laien-Modus" :-). Sollte es dafür also eine bessere Lösung geben, bitte ich dies zu entschuldigen. Zurück zum Thema...

Wie muss denn meine Abfrage aussehen, wenn sich meine Quelle auf 1;2 verändert, also mehr als eine Quelle angegeben ist...

texte
ID | Text | Quelle
------------------
1  | abc  | 1;2
------------------
2  | def  | 1;2;3
------------------
3  | ghi  | 1;3
------------------

Das Ziel soll quasi sein, dass zum Text abc zwei Quellen ausgegeben werden (in dem Fall Amazon und Ebay)

Ich habe mich mit LIKE und REGEXP versucht, aber keine ultimative Lösung gefunden. Ich meine: Gibt's eine ultimative Lösung überhaupt für das Problem oder soll ich die Quellen anders anordnen als mit Zahlenwerten?

Vielen vielen Dank!

  1. Hallo,

    Also: Ich habe zwei Tabellen in MySQL:

    texte
    ID | Text | Quelle

    1  | abc  | 1
    2  | def  | 2
    3  | ghi  | 3

    quellen
    QID | Quellenname

    1   | Amazon
    2   | Ebay
    3   | Rebuy

    Wenn ich die jetzt mit meinem laienhaften Wissen verknüpfe, funktioniert das ganz gut.

    $sql = "SELECT * FROM texte a, quelle q WHERE a.id = q.qid ORDER BY qid DESC";

      
    Naja, explizite Joins sind einfach viel schöner und klarer:  
      
    ~~~sql
    SELECT  
        *  
    FROM  
        texte t            -- besserer Aliasname als "a"  
    INNER JOIN  
        quellen q  
    ON  
        a.Quelle = q.QID   -- ist vermutlich richtiger als Dein a.id :-)  
    ORDER BY  
        qid DESC  
      
    
    

    Ich hielt es für angebracht, das über zwei Tabellen zu lösen und bin, wie gesagt, noch im "Laien-Modus" :-). Sollte es dafür also eine bessere Lösung geben, bitte ich dies zu entschuldigen. Zurück zum Thema...

    zwei Tabellen sind in dem Fall, dass es genau eine Quelle gibt, durchaus richtig.

    Wie muss denn meine Abfrage aussehen, wenn sich meine Quelle auf 1;2 verändert, also mehr als eine Quelle angegeben ist...

    wenn es zu jedem Text mehr als eine Quelle geben kann und jede Quelle mehr als einem Text zugeordnet werden kann, dann hast Du eine n:m-Beziehung und benötigst in den üblichen DBMS eine Verknüpfungstabelle. Es ist gar keine gute Idee, die Daten *nicht* atomar abzulegen.

    texte
    ID | Text |

    1  | abc  |

    2  | def  |

    3  | ghi  |

    text_quellen

    id_text | id_quelle
    -------------------
          1 |      1
          1 |      2
          2 |      1
          2 |      2
          2 |      3
          3 |      1
          3 |      3

    Die Zuordnung von Texten und Quellen erhältst Du nun über zwei Joins:

      
    SELECT  
        t.id,  
        t.text,  
        q.QID,  
        q.Quellenname  
    FROM  
        texte t  
    INNER JOIN  
        text_quellen tq  
    ON  
        t.id = tq.id_text  
    INNER JOIN  
        quellen q  
    ON  
        tq.id_quelle = q.QID
    

    Das ist die ultimative Lösung.

    Lesetipps:
    Einführung in Joins
    Fortgeschrittene Jointechniken

    Freundliche Grüße

    Vinzenz

    1. ... zwischen Tür und Angel versteh ich's noch nicht auf den ersten Blick, aber heute Abend habe ich mehr Zeit und werde es mit Hilfe der Links sicherlich hinbekommen.

      Vielen Dank schon einmal an beide Hilfestellungen/Antworten!

      1. So, vielen Dank noch einmal!

        Ich hab's (glaube ich) gecheckt, zumindest funktioniert's. Vielen Dank!

        Eine Frage habe ich noch: Wenn ich jetzt zwei Quellen zu einem Eintrag habe, erscheinen die Einträge doppelt. Also...

        abc
        Quelle: Ebay

        abc
        Quelle: Amazon

        anstatt

        abc
        Quelle: Ebay, Amazon

        Wie löse ich das denn? Da stehe ich zur Zeit aufm Schlauch

        1. Hallo,

          Eine Frage habe ich noch: Wenn ich jetzt zwei Quellen zu einem Eintrag habe, erscheinen die Einträge doppelt.

          ich würde das Ganze etwas anders lösen:

          SELECT  
             t.ID,t.Text,GROUP_CONCAT(q.Quellenname SEPARATOR ', ') as Quellen  
          FROM  
             texte t, quellen q, textquellen tq  
          WHERE  
             t.ID=tq.textID AND  
             tq.quellenID=q.QID  
          GROUP BY  
             t.ID,t.Text
          

          ich hoffe ich habe da jetzt keinen Denkfehler drinnen aber die Funktion GROUP_CONCAT() erfüllt deinen Zweck meiner Meinung nach sehr gut.

          Viele Grüße,
          Tim

          1. Hallo Tim,

            ich würde das Ganze etwas anders lösen:

            was heißt anders. Ein Einsteiger weiß noch nichts von Aggregatsfunktionen und der GROUP-BY-Klausel. Es gibt SQL-Dialekte, da darf man sich GROUP_CONCAT() selbst bauen :-(

            Wer übers Einsteigerniveau hinaus ist, verwendet keine impliziten Joins mehr :-)

            Also statt:

            SELECT

            t.ID,t.Text,GROUP_CONCAT(q.Quellenname SEPARATOR ', ') as Quellen
            FROM
               texte t, quellen q, textquellen tq
            WHERE
               t.ID=tq.textID AND
               tq.quellenID=q.QID
            GROUP BY
               t.ID,t.Text

              
              
            ~~~sql
            SELECT  
                t.id,  
                t.text,  
                [link:http://dev.mysql.com/doc/refman/5.1/en/group-by-functions.html#function_group-concat@title=GROUP_CONCAT](q.Quellenname SEPARATOR ', ') Quellen  
            FROM  
                texte t  
            INNER JOIN  
                text_quellen tq  
            ON  
                t.id = tq.id_text  
            INNER JOIN  
                quellen q  
            ON  
                tq.id_quelle = q.QID  
            GROUP BY  
                t.id,  
                t.text  
            
            

            AS für Spaltenaliasnamen vermeide ich nur aus Kompatibilitätsgründen :-)

            Freundliche Grüße

            Vinzenz

    2. Hallo Vinzenz,
      in dem Wiki-Artikel

      Fortgeschrittene Jointechniken

      ist mir aufgefallen, dass die Spaltenüberschriften von Name und Vorname vertauscht sind.
      Das war schon in dem von Felix oben verlinkten früheren Artikel so und hat offenbar den Umzug ins Wiki unbehelligt überstanden...
      Viele Grüße
      ottogal

      1. Tach!

        in dem Wiki-Artikel

        Fortgeschrittene Jointechniken
        ist mir aufgefallen, dass die Spaltenüberschriften von Name und Vorname vertauscht sind.

        Jetzt nicht mehr.

        dedlfix.

        1. Jetzt nicht mehr.

          +1

  2. Lieber Stoffel,

    texte
    ID | Text | Quelle

    1  | abc  | 1;2

    2  | def  | 1;2;3

    3  | ghi  | 1;3

    das ist Quark, da die Spalte Quelle nach Deiner Logik eine beliebig große Menge an ID enthalten kann (durch Semikola getrennt). Nach der Logik relationaler Datenbanken müsstest Du eine n:m-Beziehung mittels einer Zuweisungstabelle machen.

    texte
    ID | Text
    ---------
     1 | abc
     2 | def

    quellen
    ID | Quellenname
    ----------------
     1 | Amazon
     2 | Ebay
     3 | Rebuy

    text2quelle
    Text | Quelle
    -------------
      1  |  1
      1  |  2
      2  |  1
      2  |  2
      2  |  3
      3  |  1
      3  |  3

    Jetzt kannst Du nach Herzenslust joinen.

    Liebe Grüße,

    Felix Riesterer.

    --
    ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
  3. Und auch wenn dir bereits gesagt wurde, dass Normalisierung bzw. JOINs hier das Mittel der Wahl sind: es gibt noch eine Krücke, die sich hier verwenden lässt: FIND_IN_SET() - die ist bei kleinen Datenmengen OK, aber bei riesigen Datenmengen fürchterlich langsam.