12.7k Aufrufe
Gefragt in Skripte(PHP,ASP,Perl...) von uesch Mitglied (135 Punkte)
Hallo allerseits,

wie kann ich Daten aus einer MySQL-Tabelle so sortieren, dass das oberste Wort am ähnlichsten zum eingegebenen Wort ist und darunter die Ähnlichkeit immer mehr abnimmt? Momentan sortiert er nach dem Alphabet, aber ich möchte eine Sortierung nach der Ähnlichkeit mit dem eingegebenen Wort. Also zum Beispiel:

Eingegebenes Wort: Haus
1. Ergebnis = Haus
2. Ergebnis = Rathaus
3. Ergebnis = durchaus

Ich habe versucht, es mit levenshtein zu realisieren:
SELECT * FROM ".$table
." WHERE irgendwas like '%".$search."%' or irgendwas2 like '%".$search."%' order by ".levenshtein(???)."


Muss ich zwei Abfragen machen? Die erste fragt nach der Ähnlichkeit ab und die zweite selected dann die Datensätze und sortiert sie nach dem levenshtein-Wert? Oder kann ich das ganze mit was anderem als levenshtein machen?

Vielen Dank

23 Antworten

0 Punkte
Beantwortet von son_quatsch Experte (5.3k Punkte)
Würdest du denn alle Datensätze mit ... auswählen?
Nein, ich würde es so machen:
$res1= mysql_query( $sSql ) or die( 'Fehler: '. $sSql. ' - '. mysql_error() );

$aAbfrageErgebnisse= array();
while ($r1= mysql_fetch_array( $res1 ) ) {
$aAbfrageErgebnisse[]= $r1;
}
mysql_free_result( $res1 );
Damit sind wir bei den Grundlagen angekommen. Bitte so mit einflechten, dann die Ergebnisse (print_r()) nochmal hier posten - und möglichst nachdem du im Browser Rechtsklick -> Quelltext anzeigen gewählt hast, dann ist es besser lesbar. Und ja - deine Reihenfolge bleibt erhalten - schließlich und endlich können wir uns ja beliebig viele Kopien des Feldes erzeugen...
0 Punkte
Beantwortet von uesch Mitglied (135 Punkte)
Es wird angezeigt:
Array
(
[0] => Array
(
[0] => 5868
[id] => 5868
[1] => Haus
[deutsch] => Haus
[2] => κατοικία
[griechisch] => κατοικία
[3] =>
[zusatz] =>
[4] => Unbekannt
[artikel] => Unbekannt
[5] => Unbekannt
[wortart] => Unbekannt
[6] => 3
[genitiv] => 3
[7] =>
[gen_ersatz] =>
[8] => 0
[plural] => 0
))


Das ganze für jeden Datensatz, der die Übersetzung "Haus" enthält.
0 Punkte
Beantwortet von son_quatsch Experte (5.3k Punkte)
Und nach was gucken wir? Ich hab mich jetzt an Schlüssel [1] orientiert:

function getSimilar( $sSuch, $aErg ) {
$ar= array();
foreach( $aErg as $k1=> $v1 ) { // jeden Ergebnisdatensatz durchgehen
$iSim= similar_text( $sSuch, $v1[1] ); // wir vergleichen nur Schlüssel [1] von jedem Datensatz
$iLev= levenshtein( $sSuch, $v1[1] );
if ( $iSim!= 0 ) {
$iLevError= intval( $iLev/ strlen( $v1[1] )* 100 );
$iPerSim= intval( $iSim/ strlen( $v1[1] ) )* 100;
$iTotal= $iPerSim- $iLevError;
} else {
$iTotal= 0;
}
$ar[$k1]= $iTotal; // Schlüssel des Datensatzfeldes merken
}
arsort( $ar ); // nach errechnetem Wert sortieren
return $ar;
}


// Abfrage definieren und ausführen
$sSuchwort= 'jane';
$sSql= "SELECT *
FROM tabelle
WHERE spalte LIKE '%$sSuchwort%'";
$res1= mysql_query( $sSql ) or die( 'Fehler: '. $sSql. ' - '. mysql_error() );


// Datensätze sammeln
$aAbfrageErgebnisse= array();
while ( $r1= mysql_fetch_array( $res1 ) ) {
$aAbfrageErgebnisse[]= $r1;
}
mysql_free_result( $res1 );


// Ähnlichkeitssortierung ermitteln
$aSortiert= getSimilar( $sSuchwort, $aAbfrageErgebnisse );


// Datensätze entsprechend ihrer Ähnlichkeit ausgeben
foreach ( $aSortiert as $k1=> $v1 ) {
print_r( $aAbfrageErgebnisse[$k1] );
}
0 Punkte
Beantwortet von uesch Mitglied (135 Punkte)
Vielen Dank!
Das hat mir sehr geholfen.
0 Punkte
Beantwortet von uesch Mitglied (135 Punkte)
Doch noch eine kurze Frage. Um das ganze mit Seitenzahlen zu bewerkstelligen, muss ich mit LIMIT arbeiten. Wenn ich allerdings folgendes mache:
WHERE spalte LIKE '%$sSuchwort%' LIMIT 0,10
erhalte ich zwar 10 Datensätze, allerdings fängt das Ganze nicht bei dem Wort an, bei dem es ohne LIMIT anfängt. Also zum Beispiel bei Eingabe von "Hand" erhalte ich nicht als erstes "Hand", sondern "Handy".

Könnt Ihr euch erklären, warum?

Viele Grüße von Üsch
0 Punkte
Beantwortet von uesch Mitglied (135 Punkte)
Ach, das liegt natürlich daran, dass nur noch innerhalb der 10 Datensätze nach Ähnlichkeiten gesucht wird.

Wie würdet Ihr denn dann die Beiträge limitieren? Also dass zum Beispiel auf Seite 1 die 10 ähnlichsten Datensätze angezeigt werden, auf Seite 2 dann die Datensätze von 10 bis 20 usw.

Ich hoffe, das liest überhaupt noch jemand, da ich bereits angegeben habe, dass "Diese Antwort das Problem gelöst hat!".

Schöne Grüße von Üsch!
0 Punkte
Beantwortet von son_quatsch Experte (5.3k Punkte)
Da gibt es mehrere Herangehensweisen. Die simpelste davon ist, dass du jedesmal die komplette Abfrage durchführst und dann bei der Ausgabe entsprechend limitierst.

// Datensätze entsprechend ihrer Ähnlichkeit ausgeben
$i1= 0;
foreach ( $aSortiert as $k1=> $v1 ) {
if ( $i1>= $iStart&& $i1< $iStart+ $iAnzahl ) print_r( $aAbfrageErgebnisse[$k1] );
$i1++;
}
...wobei $iStart und $iAnzahl in dieser Reihenfolge dieselbe Funktion übernehmen wie die Angaben bei LIMIT unter MySQL.
0 Punkte
Beantwortet von uesch Mitglied (135 Punkte)
Danke. Wenn $iStart = 0 und $iAnzahl = 10 ist, dann funktioniert es. Es werden 10 Datensätze angezeigt. Wenn jetzt aber $iStart = 10 und $iAnzahl = 20 ist, wird nichts angezeigt.

Ist in diesem Fall nicht $i1 gleich Null? Und deshalb kann
$i1>= $iStart
nicht true zurückliefern?

Es wird allerdings auch kein Ergebnis zurückgeliefert, wenn ich $i1 gleich 10 setze.
0 Punkte
Beantwortet von son_quatsch Experte (5.3k Punkte)
Mit $i1 zählen wir lediglich, wie oft wir die Schleife durchlaufen - das scheint wohl alles andere als offensichtlich. Folglich wollen wir bei einem $iStart größer als 0 ein paar der ersten Ergebnisse "wegschmeißen" und dann erst welche anzeigen.

Ich wette du hast einfach noch das LIMIT in deiner MySQL-Abfrage drin. Nimm das raus.
0 Punkte
Beantwortet von uesch Mitglied (135 Punkte)
Am Anfang definierst du doch
$i1= 0;

Und das
$i1++;
steht innerhalb der if-Klausel. Das heißt, dass das $i1 (=0)überhaupt nicht verändert wird, weil ja $i1 auf jeden Fall kleiner ist als $iStart.
Wenn ich jetzt das
$i1++;
außerhalb der if-Klausel innerhalb des foreach einfüge, dann werden mir die korrekten Datensätze angezeigt, nur dass es mit jeder Seite jeweils 10 mehr werden (weil eben keine Datensätze "weggeschmissen" werden.

PS: Limit war nicht mehr in der Abfrage.
...