Marc Reichelt: (MYSQL) Suche

Hallo an alle,

ich bin momentan an einem Datenbankprojekt dran, und habe nun ja auch schon oft das Forum dafür bemüht. Vor einem Problem stehe ich nun wieder, dabei scheint es auch wieder ganz einfach zu sein: Eine Suche über zwei Felder.

Die Suche ist derart aufgebaut, dass es einen Suchstring gibt, dessen Wörter alle als Einzelbegriffe behandelt werden, es muss also jedes Wort gefunden werden (AND-Verknüpfung):

Search for: [                       ]
            [x] search in artist name
            [x] search in song name

Dabei soll man aus zwei (oder später vielleicht auch mehr) Feldern wählen können, also zu Beginn erst mal der Name des Interpreten ('artistname') und der Name des Songs ('songname').

Jetzt habe ich z.B. eine Suche nach Name und Interpret, und der Suchstring ist im Beispiel "Hosen Sehnsucht".
Dabei soll natürlich das Lied "Die Toten Hosen - Ich Bin Die Sehnsucht In Dir" gefunden werden.
Problem dabei ist, dass Name des Interpreten und Name des Songs in unterschiedlichen Tabellen liegen und vor der Suche erst irgendwie verknüpft werden müssen, also dass ich eine Spalte habe die für jedes Lied die Verknüpfung der beiden Felder enthält.
Wie erstelle ich solch eine Suche in MySQL am klügsten?

Diese Seite habe ich während meiner Suche in Google übrigens gefunden:
http://dev.mysql.com/doc/mysql/de/string-comparison-functions.html

Vielen Dank & 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. Hi,

    Problem dabei ist, dass Name des Interpreten und Name des Songs in unterschiedlichen Tabellen liegen und vor der Suche erst irgendwie verknüpft werden müssen, also dass ich eine Spalte habe die für jedes Lied die Verknüpfung der beiden Felder enthält.

    wie sieht die Verknuepfung denn so aus?

    Detailangaben zum Datendesign sind durchaus willkommen.

    Gruss,
    Ludger

    1. Hallo Ludger,

      Problem dabei ist, dass Name des Interpreten und Name des Songs in unterschiedlichen Tabellen liegen und vor der Suche erst irgendwie verknüpft werden müssen, also dass ich eine Spalte habe die für jedes Lied die Verknüpfung der beiden Felder enthält.

      wie sieht die Verknuepfung denn so aus?

      Detailangaben zum Datendesign sind durchaus willkommen.

      Also, ich habe (sehr stark vereinfacht) folgende Struktur:

      1. Tabelle 'artists':

      id | name
      ---------------------
      42 | Die Toten Hosen

      1. Tabelle 'songs':

      id  | name                         | artist_id
      -----------------------------------------------
      666 | Ich Bin Die Sehnsucht In Dir | 42

      Da kommen natürlich noch wesentlich mehr Zeilen, aber das ist das Beispiel.
      Über einen INNER JOIN bekomme ich schon mal die Tabellen verknüpft.

      Beispiel:

        
      SELECT  
       artists.name AS artistname,  
       songs.name AS songname,  
       artists.id AS artist_id,  
       songs.id AS songs_id  
      FROM songs  
      INNER JOIN artists ON songs.artist_id=artists.id;  
      
      

      Nun möchte ich aber gleich mit dieser Abfrage eine Suche durchführen, wie ich in meinem ersten Post beschrieben habe - und am Ende nur noch die gefundenen Zeilen haben.

      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. Hi,

        Über einen INNER JOIN bekomme ich schon mal die Tabellen verknüpft.

        Beispiel:

        SELECT
        artists.name AS artistname,
        songs.name AS songname,
        artists.id AS artist_id,
        songs.id AS songs_id
        FROM songs
        INNER JOIN artists ON songs.artist_id=artists.id;

        
        >   
        > Nun möchte ich aber gleich mit dieser Abfrage eine Suche durchführen, wie ich in meinem ersten Post beschrieben habe - und am Ende nur noch die gefundenen Zeilen haben.  
          
        ich muesste jetzt eigentlich die  
          
        \_\_\_WHERE\_\_\_  
          
        -Klausel empfehlen, habe aber den Verdacht, dass Du diese schon kennst. Was ist denn da ueberhaupt das Problem? Du machst Dir Performanceueberlegungen?  
          
        Gruss,  
        Ludger
        
        1. Hallo Ludger,

            
          SELECT  
           artists.name AS artistname,  
           songs.name AS songname,  
           artists.id AS artist_id,  
           songs.id AS songs_id  
           FROM songs  
           INNER JOIN artists ON songs.artist_id=artists.id;  
          
          

          ich muesste jetzt eigentlich die

          ___WHERE___

          -Klausel empfehlen, habe aber den Verdacht, dass Du diese schon kennst. Was ist denn da ueberhaupt das Problem? Du machst Dir Performanceueberlegungen?

          Die WHERE-Klausel kenne ich und wende sie schon sinnvoll in vielen anderen Projekten ein. ;-)
          Das was ich wissen möchte ist das, was ich hinter WHERE schreiben muss, um möglichst schnell meine gewünschte Suche zu vollziehen.
          Dabei sind Performanceüberlegungen sogar ganz angebracht, schließlich wird die Suche öfters betätigt werden.

          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,

            Dabei sind Performanceüberlegungen sogar ganz angebracht, schließlich wird die Suche öfters betätigt werden.

            schau dir mal die Volltextsuche in der mysql doku an, die sollte was für dich sein.

            Ilja

  2. echo $begrüßung;

    Wenn du nach mehreren Begriffen, die in einer Spalte vorkommen können sollen, suchen möchtest bietet sich IN an: WHERE artist IN ('begriff1','begriff2','begriff3') OR song IN ('begriff1',...). Diese Liste empfiehlt sich mit PHP zusammenbauen, da sie beliebig viele Werte enthalten kann und eine Auswertung mit SQL-Syntax wenn überhaupt dann nur sehr aufwändig zu realisieren wäre.

    Doch leider möchtest du sicher nicht nur nach ganzen Wörtern sondern auch nach teilweisen Vorkommen suchen. "Hosen" sollen auch in "Die Toten Hosen" gefunden werden. Damit brauchst du ein LIKE: WHERE artist LIKE '%hosen%'. Und solch eine Bedingung müsstest du für jeden Suchbegriff und für beide Spalten an das Statement mit PHPs Hilfe anhängen. Eine Verknüpfung mit LIKE IN gibt es nicht.

    Was gibt es noch? Reguläre Ausdrücke mit REGEXP/RLIKE. Regexp kennt eine Oder-Verknüpfung, und damit sieht eine Lösung so aus: WHERE artist RLIKE 'begriff1|begriff2|begriff3' OR song RLIKE ...
    Die Liste der Suchwörter kannst du einfach über PHP erstellen: implode('|', explode(' ', $suche))
    Vorher solltest du noch alle nicht-alphanumerischen Zeichen aus $suche verbannen, sonst wirft dir im besten Falle MySQL eine Fehlermeldung zu.

    echo "$verabschiedung $name";

    1. Hallo dedlfix,

      Wenn du nach mehreren Begriffen, die in einer Spalte vorkommen können sollen, suchen möchtest bietet sich IN an: WHERE artist IN ('begriff1','begriff2','begriff3') OR song IN ('begriff1',...). Diese Liste empfiehlt sich mit PHP zusammenbauen, da sie beliebig viele Werte enthalten kann und eine Auswertung mit SQL-Syntax wenn überhaupt dann nur sehr aufwändig zu realisieren wäre.

      Den Suchstring bastel ich auf jeden Fall mit PHP zusammen, das ist klar. :-)

      Doch leider möchtest du sicher nicht nur nach ganzen Wörtern sondern auch nach teilweisen Vorkommen suchen. "Hosen" sollen auch in "Die Toten Hosen" gefunden werden. Damit brauchst du ein LIKE: WHERE artist LIKE '%hosen%'. Und solch eine Bedingung müsstest du für jeden Suchbegriff und für beide Spalten an das Statement mit PHPs Hilfe anhängen. Eine Verknüpfung mit LIKE IN gibt es nicht.

      Schade, auf so eine Möglichkeit habe ich insgeheim gehofft.
      Nun denn, dann bastel ich mir das Ganze mit PHP zusammen - ausgeführt wird es aber ja immer noch via MySQL.

      Was gibt es noch? Reguläre Ausdrücke mit REGEXP/RLIKE. Regexp kennt eine Oder-Verknüpfung, und damit sieht eine Lösung so aus: WHERE artist RLIKE 'begriff1|begriff2|begriff3' OR song RLIKE ...

      Hmm, da gefällt mir die Variante LIKE "%hosen%" besser, denn reguläre Ausdrücke brauchen mehr Zeit und ich benötige sie in meinem konkreten Fall nicht. Und die Oder-Verknüpfungen mache ich dann ganz einfach mit MySQL.

      Jetzt aber mein Problem, nochmals zum Ausgangsbeispiel zurück:
      WHERE
        (artist LIKE "%Hosen%" AND artist LIKE "%Sehnsucht%") OR
        (song LIKE "%Hosen%" AND song LIKE "%Sehnsucht%")

      liefert mir _nicht_ das Ergebnis "Die Toten Hosen - Ich Bin Die Sehnsucht In Dir".
      Warum? Ganz einfach - in "Die Toten Hosen" steckt zwar das Wort "Hosen", aber nicht das Wort "Sehnsucht". Umgekehrt steckt im Songnamen nur das Wort "Sehnsucht" drin.

      Eine solche Suche möchte ich nicht, da ich damit unter anderem alle Lieder der Toten Hosen bekomme, obwohl der Suchbegriff "Sehnsucht" mit gemeint ist.

      Wahrscheinlich müsste ich es dann eher so aufbauen:
      WHERE
       (artist LIKE "%Hosen%" OR song LIKE "%Hosen%") AND
       (artist LIKE "%Sehnsucht%" OR song LIKE "%Sehnsucht%")

      Und vor solchen Konstrukten wollte ich weg, da ich dachte dass es in MySQL vielleicht so etwas wie eine kurzfristige Verknüpfung mehrerer Spalten gibt.
      So etwas meinte ich (funktioniert natürlich nicht):
      WHERE artist+song LIKE "%Hosen%" AND artist+song LIKE "%Sehnsucht%"

      Aber verwenden tue ich nun die obrige Lösung.

      [REGEXP]
      Vorher solltest du noch alle nicht-alphanumerischen Zeichen aus $suche verbannen, sonst wirft dir im besten Falle MySQL eine Fehlermeldung zu.

      Wie gesagt, reguläre Ausdrücke müssen nicht unbedingt sein, das Escapen von zwei Zeichen bei LIKE (% und _) genügt mir vollkommen.

      Grüße & vielen Dank für die Antworten

      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,

        Hmm, da gefällt mir die Variante LIKE "%hosen%" besser, denn reguläre Ausdrücke brauchen mehr Zeit ...

        Bitte einmal Zeiten messen.

        LIKE %irgendwas%

        ist langsam und aufwendig.

        Wahrscheinlich müsste ich es dann eher so aufbauen:
        WHERE
        (artist LIKE "%Hosen%" OR song LIKE "%Hosen%") AND
        (artist LIKE "%Sehnsucht%" OR song LIKE "%Sehnsucht%")

        Und vor solchen Konstrukten wollte ich weg, da ich dachte dass es in MySQL vielleicht so etwas wie eine kurzfristige Verknüpfung mehrerer Spalten gibt.
        So etwas meinte ich (funktioniert natürlich nicht):
        WHERE artist+song LIKE "%Hosen%" AND artist+song LIKE "%Sehnsucht%"

        Schon mal unter Volltextsuche nachgeschaut?

        Freundliche Grüße

        Vinzenz

        1. Hallo Vinzenz,

          Schon mal unter Volltextsuche nachgeschaut?

          Sehr schön, ich glaube FULLTEXT ist doch das was ich benötige.
          Ich werde auch mal einen Beispiel-Performance-Test durchführen, bin mir aber sicher dass eine Abfrage mit LIKE hier langsamer sein wird - obwohl die Spalten so kurz sind dass ich eigentlich dachte keinen Volltext verwenden zu müssen.

          Vielen Dank und 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
      2. echo $begrüßung;

        Hmm, da gefällt mir die Variante LIKE "%hosen%" besser, denn reguläre Ausdrücke brauchen mehr Zeit und ich benötige sie in meinem konkreten Fall nicht.

        Wenn du wirklich Wert auf Geschwindigkeit legst, dann überlasse das nicht deinem Bauchgefühl sondern probier das aus. Es könnte gut sein, das das Zusammenbauen in PHP länger dauert als die RegExp-Maschine in MySQL braucht, zumal diese weniger kann als Perl-Regexp und somit vielleicht auch recht schnell sein könnte.

        Und vor solchen Konstrukten wollte ich weg, da ich dachte dass es in MySQL vielleicht so etwas wie eine kurzfristige Verknüpfung mehrerer Spalten gibt.
        So etwas meinte ich (funktioniert natürlich nicht):
        WHERE artist+song LIKE "%Hosen%" AND artist+song LIKE "%Sehnsucht%"

        Das ist auch eine Idee, Strings/Stringfelder verknüpft man aber mit der Funktion CONCAT().

        "+" geht nur für die mathematische Addition. Einen Fehler erzeugt MySQL bei Strings trotzdem nicht, weil es wegen der automatischen Typumwandlung in den Strings Zahlen zu erkennen versucht und dabei bei Nicht-Zahlen 0 nimmt.

        echo "$verabschiedung $name";