Tschjensi: SQL Suche nach Ähnlichkeit

Hallo, Ich möchte Vornamen aus einer SQL Datenbank nach Ähnlichkeit eines Suchwortes sortieren lassen. Gibt es hierfür eine Funktion, oder muss ich die Daten in einen Array bringen und diesen einzelnt aufstückeln? Beispiel Vornamen in Datenbank Sofia, Sophia, Sofie, Sandra, Svenja, Sonja, Stefan Suchwort: Sof

  1. da gibts ja gleich mehrere zur Auswahl:

    fuzzystrmatch

  2. Hello,

    und ein Link von mir auf einen interessanten Thread zum Thema "MySQL Soundex auf Deutsch"

    Vielleicht kannst du etwas damit anfangen, dann berichte mal bitte.

    Mit einer zusätzlichen Spalte/Tabelle in MySQL könntest Du die ähnlich klingenden DS auch abspeichern. Ich empfehle dafür eine benutzerdefinierte Funktion in MySQL (siehe verlinkten Thread) für das Erstellen der Ähnlichkeitsmaske und Triggers für "Insert before" und "Update before". Dann bleibt die Spalte/Tabelle auch immer aktuell.

    Liebe Grüße
    Tom S.

    --
    Es gibt nichts Gutes, außer man tut es
    Andersdenkende waren noch nie beliebt, aber meistens diejenigen, die die Freiheit vorangebracht haben.
  3. Tach!

    Ich möchte Vornamen aus einer SQL Datenbank nach Ähnlichkeit eines Suchwortes sortieren lassen. Gibt es hierfür eine Funktion, oder muss ich die Daten in einen Array bringen und diesen einzelnt aufstückeln?

    Was hast du dir denn da vorgestellt, wie du vorgehst, wenn du es zu Fuß machen wollen würdest?

    Die Frage ist, wie definiert man Ähnlichkeit. Sollen bestimmte Buchstaben(kombinationen) gleichwertig zu anderen sein, oder soll es nach dem Klang der Aussprache gehen? In jedem Fall gibt es je nach Sprache Unterschiede, was da ähnlich zu wem ist. Üblicherweise sind die eingebauten Algorithmen nur auf Englisch ausgelegt. Und für MySQL fällt mir auch nur SOUNDEX() ein.

    dedlfix.

  4. Mit den Funktionen, die Datenbanken zur Verfügung stellen, habe ich überwiegend schlechte Erfahrungen gemacht. Schlimmer noch als zu viele unpassende Vorschläge war, dass oft genug gar keine kamen, obwohl welche hätten kommen müssen. Sprich: Was ich als ähnlich empfand, war oft genug nicht das, was auch die Datenbank für ähnlich hielt. Sich durch einen Wurst an Vorschlägen wühlen zu müssen, ist unbequem, Einträge doppelt und dreifach anzulegen, weil die passenden Vorschläge nicht kamen, ist hingegen ein Problem.

    Schlussendlich bin ich dann auf N-Gramme umgestiegen. Die Daten werden in Zwei- bis Drei-Buchstaben-Stücke zerlegt (Sophie -> so, sop, oph, phi, hie, ie) und diese in einer separaten Tabelle gespeichert.
    Das Suchwort wird auf die gleiche Weise zerlegt. Die Stücke des Suchwortes werden in dem Datenstückwust gezählt und mit ihren Daten aufsummiert. Je höher die Summe eines Datums, d.h. je mehr Treffer ein Datum erzielt, desto besser passt es zum Suchwort.

    Die Datenhaltung ist natürlich etwas aufwändiger, aber das Ergebnis gemessen am simplen Prinzip meines Erachtens bisweilen erstaunlich – und allemal besser als soundex(), levenshtein() & Co.

    Beispiel:

    Suchwort sof   -> so sof of
             sofi  -> so sof ofi fi
             sofia -> so sof ofi fia ia
             sofie -> so sof ofi fie ie
    
                                      sof  sofi  sofia  sofie
    Sophie -> so sop oph phi hie ie    1     1     1      2
    Sofie  -> so sof ofi fie ie        2     3     1      5
    Sophia -> so sop oph phi hia ia    1     1     2      1
    
    

    Das Ganze lässt sich natürlich noch verfeinern, manche verwenden Stücke à bis zu vier Buchstaben, manche zählen Worttrennungen oder Anfangs- und Endebezeichner mit und sogar höherwertig. Ob der Aufwand den Nutzen rechtfertigt, hängt sicher vom Einsatzzweck ab. Sinnig dürfte als Erstes eine Normalisierung der Daten sein; im Beispiel oben habe ich nur Kleinbuchstaben verwendet, das Entfernen von Satzzeichen bzw. Ersetzen durch ein Leerzeichen kann auch merklich helfen.

    1. aus Neugier, ist das das hier (bloss mit n statt 3)?

      Faster PostgreSQL Searches with Trigrams

      1. aus Neugier, ist das das hier (bloss mit n statt 3)?

        Faster PostgreSQL Searches with Trigrams

        Ja. Dort werden durch Voranstellen von zwei Leerzeichen zusätzlich auch die Wortanfänge bewertet.

  5. Danke schon einmal im Vorraus für die grosse Beteiligung an meinen Beitrag.

    @ dedlfix

    Was hast du dir denn da vorgestellt, wie du vorgehst, wenn du es zu Fuß machen wollen würdest?

    Ich denke das es genau das Problem trifft, man wünscht sich eine Funktion die es wahrscheinlich gar nicht gibt.

    @Regina Schlauklug trifft es ganz gut

    Mit den Funktionen, die Datenbanken zur Verfügung stellen, habe ich überwiegend schlechte Erfahrungen gemacht.

    Da jeder ja seine eigenen Vorstellungen hat was "Ähnlich" bedeutet, wird es der beste Ansatz sein eigene Äquivalenzen zu setzen. Bsp:

    C = K #Karl- Carl

    pf = f #Sopfie- Sofie

    In meinen Fall mit den Vornamen wird es Sinn machen einen Vornamen als Gruppe in eine weitere Datenbank zu schreiben und diese gesammte Gruppe dann als Suchwörter für die erste DB verwenden.

    Da ich die erste DB aber nur einmal durchsuchen möchte um nur ein Array zu erhalten stellt sich die Frage gibt es eine SQL Funktion die mehrere Suchbegriffe eischliesst?

    $sql = "SELECT * FROM Personen WHERE Vorname LIKE '*Sofie,Sophia,Sophie*' ";
    
    1. Hallo Tschjensi,

      Da ich die erste DB aber nur einmal durchsuchen möchte um nur ein Array zu erhalten stellt sich die Frage gibt es eine SQL Funktion die mehrere Suchbegriffe eischliesst?

      $sql = "SELECT * FROM Personen WHERE Vorname LIKE '*Sofie,Sophia,Sophie*' ";
      

      OR existiert.

      SELECT * ist häufig keine gute Idee.

      Bis demnächst
      Matthias

      --
      Rosen sind rot.
    2. Hallo,

      pf = f

      Das Pferd heißt Pferd, weil es fährt⁉️

      scnr

      Gruß
      Kalk

      1. Hi,

        pf = f Das Pferd heißt Pferd, weil es fährt⁉️

        Nein, Pferde heißen Pferde, weil sie auf der Erde laufen. Flögen sie durch die Lüfte, hießen sie Pflüfte.

        cu,
        Andreas a/k/a MudGuard

    3. Da jeder ja seine eigenen Vorstellungen hat was "Ähnlich" bedeutet, wird es der beste Ansatz sein eigene Äquivalenzen zu setzen. Bsp:

      C = K #Karl- Carl

      pf = f #Sopfie- Sofie

      Ohne jetzt auf meinem Vorschlag rumreiten zu wollen: Mit solchen gezielten Äquivalenzen wirst du immer irgendwas übersehen, sie sind zu speziell. Oder du "äquivalenzifizierst" so dermaßen, dass kaum noch Unterschiede zu erkennen sind. Im Grunde baust du damit den gleichen Kram, der bereits als soundex() etc. existiert.

      Mit n-Grammen hingegen würden deine beiden Beispiele schon von Haus aus als nahezu identisch ausgegeben, ohne, dass du dir Gedanken darüber machen musst, welche Buchstabenkombinationen du als gleichwertig betrachten möchtest. Wie ich bereits schrieb: So simpel das Prinzip auch sein mag, die Leistung ist enorm.

      In meinen Fall mit den Vornamen wird es Sinn machen einen Vornamen als Gruppe in eine weitere Datenbank zu schreiben und diese gesamte Gruppe dann als Suchwörter für die erste DB verwenden.

      Das kommt mir nun wirklich äußerst aufwändig und fehlerträchtig vor. Weisst du, wie viele Vornamen es gibt, die gewollt beknackten ("Lennoks Mädden") noch nicht einmal mitgezählt?

      Und wenn du schon selbst eine weitere Tabelle vorsiehst … für die Suche nach Vorschlägen reicht ein einzelner SQL-Befehl:

      
      > select count(*) as wert,person.name from person join ngramm on person.id = ngramm.person where gramm in ("so", "sof", "of") group by person.id order by wert desc;
      
      +------+--------+
      | wert | name   |
      +------+--------+
      |    2 | Sofia  |
      |    2 | Sofie  |
      |    1 | Sophia |
      +------+--------+
      
      
      > show columns from person;
      
      +-------+--------------+------+-----+---------+----------------+
      | Field | Type         | Null | Key | Default | Extra          |
      +-------+--------------+------+-----+---------+----------------+
      | id    | int(11)      | NO   | PRI | NULL    | auto_increment |
      | name  | varchar(100) | NO   |     | NULL    |                |
      +-------+--------------+------+-----+---------+----------------+
      
      
      > show columns from ngramm;
      
      +--------+---------+------+-----+---------+-------+
      | Field  | Type    | Null | Key | Default | Extra |
      +--------+---------+------+-----+---------+-------+
      | gramm  | char(3) | NO   | MUL | NULL    |       |
      | person | int(11) | NO   |     | NULL    |       |
      +--------+---------+------+-----+---------+-------+
      
      
      
      1. Ich glaube das Querdenkerprinzip bei mir ist das z.B. Bei der suche nach: "Anna Sophie" auch der Eintrag "Sofia" gelistet werden soll.

        Und da die beiden Begriffe Logisch gesehen nicht zusammen passen muss hier eine Verknüfung gesetzt werden.

        Hier einmal mein bisheriger Quelltext, ich hoffe ich bin hier auf den richtigen Weg. zwar ist es richtig das das hinzufügen von 'Referenzbegriffen' nicht der bequemste Ansatz ist, aber meine jetzige Suche basiert auf "Zeige mir ähnlich logische Einträge" sowie "Ich wünsch mir nen Ponnyhof"-Eintrag 😉

        if ($_POST["suchbegriff"]){
        		// Mysql Abfrage wird gespeichert mit den Notwendigen Parameter
        
        $AQtext = "";
        $teile = explode(" ", $_POST["suchbegriff"]);
        foreach($teile as $key=>$value){
        	#echo "Index " . $key . " hat den Wert " . $value . "<br>";
        if ($value != ""){		
        		$AQtext = $AQtext." OR Vorname  LIKE ('%".mysql_real_escape_string(utf8_decode($value ))."%')";
        		#echo $AQtext."<br>";
        
        		$sql = "SELECT * FROM Aequivalente WHERE Begriff LIKE ('%".mysql_real_escape_string(utf8_decode($value ))."%')";
        		$queryresult = mysql_query($sql);
        		while($row = mysql_fetch_object($queryresult)){
        			$ZeileAQ[$i] = utf8_encode($row->Begriff);
        			$Gruppe = utf8_encode($row->Gruppe);
        			#echo $Gruppe;
        			#echo "<br/>";
        			$i++;	
        		}
        		
        		$sql = "SELECT * FROM Aequivalente WHERE Gruppe='$Gruppe'";
        		$queryresult = mysql_query($sql);
        		while($row = mysql_fetch_object($queryresult)){
        			$ZeileAQ = utf8_encode($row->Begriff);
        			#$Gruppe = utf8_encode($row->Gruppe);
        			#echo $ZeileAQ[$i];
        			#echo "<br/>";
        			$AQtext = $AQtext." OR Vorname  LIKE ('%".mysql_real_escape_string(utf8_decode($ZeileAQ ))."%')"; 
        			$i++;	
        		}
        
        		} 
        
        
        }
        
        $sql = "SELECT * FROM ".$tabelle." WHERE Vorname LIKE ('%".mysql_real_escape_string(utf8_decode($_POST["suchbegriff"]))."%') $AQtext ";
        #echo $sql;
        		// Mysql Abfrage wird durchgeführt
        		$result = mysql_query($sql);
        		
        		
        		$i=0;
        		while($row = mysql_fetch_object($result)){
        			$Zeile[$i] = utf8_encode($row->Vorname);
        			#echo $Zeile[$i]."<br>";
        			$i++;	
        		}
        
  6. Moin,

    SQL oder MySQL? In MySQL gibt es die möglichkeit einer integrierten Volltextsuche mit ähnlichkeitsfindung. Stichworte match und against

    Gruß Bobby

    --
    -> Für jedes Problem gibt es eine Lösung, die einfach, sauber und falsch ist! <- ### Henry L. Mencken ### -> Nicht das Problem macht die Schwierigkeiten, sondern unsere Sichtweise! <- ### Viktor Frankl ### ie:{ br:> fl:{ va:} ls:< fo:) rl:( n4:( de:> ss:) ch:? js:( mo:} sh:) zu:)