Abfrageergebnis nach Anzahl erfüllter Bedinungen sortieren



  • Hallo,

    und zwar habe ich eine Tabelle, in der es eine Spalte gibt, in der Text steht.
    Nun möchte ich die Datensätze nach bestimmten Wörtern durchsuchen. Z.B. "bla" und "blubb" und "ups". Wenn mindestens eines der Wörter im Text vorkommt, ist die Bedingung für diesen Datensatz erfüllt. Ich möchte dann aber aus allen Datensätzen mit diesen Wörtern den Datensatz zuerst haben, der die meisten Wörter im Text hat. Also das Abfrageergebnis soll anhand der Anzahl der erfüllten Bedingungen sortiert werden.

    So ungefähr:

    SELECT id FROM tabelle WHERE text LIKE '%bla%' OR text LIKE '%blubb%' OR text LIKE '%ups%' ORDER BY ????
    

    Mir fällt dafür nur eine Lösung ein, und zwar nur die, dass ich halt mehrere Abfragen mache und je nachdem ein Wort weniger hinters WHERE schreibe und die Bedingungen mit AND verknüpfe.

    Hat jemand vlt. eine elegantere Methode? Weil meine Idee finde ich nicht sehr praktisch, weil die Anzahl der Wörter kann auch größer als Drei sein.

    Grüße
    Jan



  • Ich denke nicht, dass man dieses effizient mit SQL ausdrücken kann. Eine Möglichkeit wäre, nach jeden Suchberiff extra zu suchen und die einzelnen Ergebnisse mit UNION ALL zusammenzufassen, gruppieren und dann sollte eine Tabelle entstehen mit id, Anzahl gefundener Begriffe. Aber das wird sehr teuer werden. Wenn es umfangreiche Daten sind kann auch eine Textindizierung mit entsprechenden Indizies erwogen werden.



  • Du könntest über einen CASE-Block Prioritäten vergeben und danach sortieren

    ...

    ORDER BY CASE WHEN text LIKE '%bla%'   THEN 1
                  WHEN text LIKE '%blubb%' THEN 2 
                  ... END ASC;
    


  • Das mit dem CASE-Block hört sich interessant an, das werde ich morgen mal ausprobieren. Ansonsten bin ich über die Volltextsuche gestolpert. Bringt aber noch nicht genau das, was ich will, aber vlt. gibt es da noch ein paar Einstellungen, die ich noch nicht kenne.



  • Das würde aber dazu führen, dass wenn nur die letzte Bedingung erfüllt ist, der Eintrag nicht gleichwertig sortiert wird mit einem, bei dem nur die erste Bedingung erfüllt ist. (Zumindest glaube ich das ^^)

    Meine Idee wäre den genannten Ansatz so umzuformulieren, das man neben Id noch eine weiteres Datenfeld einfügt und bei dem z.B. die boolean-Werte addiert (true sollte ja hoffentlich 1 sein, kann die DB bool nach int casten?)

    Select id, (text LIKE '%bla%') + (text LIKE '%blubb%') AS similarity FROM ... ORDER BY similarity
    (oder evtl. ORDER BY 2, je nach DB)

    Ist jetzt mehr oder weniger Pseudocode. Musst du mal ausprobieren wie die DB die Syntax wirklich haben will.

    mfg
    xXx



  • Hallo,

    der erste Versuch stimmt nicht ganz, da sich bei mehreren Bedingungen die Priorität nicht aufsummert. Hier mal ein Beispiel, wie's gehen sollte (Oracle tested), wobei die obere CASE-Anweisung nur zur Prüfung drin ist.

    SELECT 
      CASE WHEN t.TABLE_NAME LIKE '%S%' THEN 1 ELSE 0 END +
      CASE WHEN t.TABLE_NAME LIKE '%A%' THEN 1 ELSE 0 END +
      CASE WHEN t.TABLE_NAME LIKE '%L%' THEN 1 ELSE 0 END +
      CASE WHEN t.TABLE_NAME LIKE '%G%' THEN 1 ELSE 0 END AS PRIORITY,  
      t.TABLE_NAME
    FROM  USER_TABLES t
    WHERE 
      t.TABLE_NAME LIKE '%S%'
      OR    t.TABLE_NAME LIKE '%A%'
      OR    t.TABLE_NAME LIKE '%L%'
      OR    t.TABLE_NAME LIKE '%G%'
    ORDER BY
      CASE WHEN t.TABLE_NAME LIKE '%S%' THEN 1 ELSE 0 END +
      CASE WHEN t.TABLE_NAME LIKE '%A%' THEN 1 ELSE 0 END +
      CASE WHEN t.TABLE_NAME LIKE '%L%' THEN 1 ELSE 0 END +
      CASE WHEN t.TABLE_NAME LIKE '%G%' THEN 1 ELSE 0 END DESC;
    


  • Der Vorschlag von -=]xXx[=- funktioniert wunderbar! Genau das habe ich gebraucht! Vielen Dank!
    Was praktisch wäre, wäre, wenn man irgendwie dann noch die Ergebnisse mit similarity>0 eingrenzen könnte. Momentan mache ich das halt so, dass ich in der Where-Klausel "text LIKE '%bla%' OR text LIKE '%blubb%'" stehen habe.



  • Ich denke da spricht nichts dagegen...

    Select id, (text LIKE '%bla%') + (text LIKE '%blubb%') AS similarity FROM table WHERE similarity > 0 ORDER BY similarity
    

    dann können die anderen Vergleiche in der Where-Clause alle weg.

    mfg
    xXx



  • Das funktioniert leider nicht, der findet keine Spalte "similarity".



  • Was für eine Datenbank nutzt du?

    Hab mal gerade by Mysql nachgesehen:
    Der standard verbietet das wohl. Mir war da auch was im Hinterkopf, hatte das aber unter ORDER und GROUP verbucht.

    Jedenfalls könnte es mit HAVING gehen:

    SELECT ... FROM table ORDER BY similarity GROUP BY id HAVING similarity > 0
    

    u.U. ist die Query aber langsamer / Speicherlastiger als eine in der du alle vergleiche in der WHERE-Clause stehen hast, da HAVING quasi nach ausführen der Query gemacht wird, kurz bevor du das Ergebnis bekommst.

    mfg
    xXx



  • Bei Oracle kann man in der WHERE-Klausel keinen ALIAS verwenden, also must du den similarity-Ausdruck ausschreiben oder du verwendest die eigentliche Abfrage als Subselect:

    SELECT *
    FROM   (
      SELECT 
        id,
        [..] AS similarity 
      FROM table ) t
    WHERE t.similarity > 0
    ORDER BY t.similarity DESC;
    

    Bei 'GROUP BY' wird der Kode möglicherweise recht umständlich, weil du jedes Feld, dass du ausgeben möchtest und das nicht in GROUP BY auftaucht, in eine Funktion (z.B. max()) gießen musst. So sieht das ORACLE zumindest.



  • Bei 'GROUP BY' wird der Kode möglicherweise recht umständlich, weil du jedes Feld, dass du ausgeben möchtest und das nicht in GROUP BY auftaucht, in eine Funktion (z.B. max()) gießen musst. So sieht das ORACLE zumindest.

    Das verstehe ich jetzt nicht so ganz. Sofern id einzigartig ist, passt doch das was ich geschrieben hab. Oder beziehst du dich jetzt auf etwas anderes?

    mfg
    xXx



  • @xXx: Wenn nur die ID interessiert, dann passt die Lösung. Wenn des SELECT allerdings weitere Spalten ausgeben soll, liefert die DB (ORACLE) eine Fehlermeldung, selbst wenn die ID eindeutig ist und somit nur ein Datensatz 'zusammengefaßt' wird. Das gilt auch für den Ausdruck similarity.

    SELECT 
      t.id, 
      t.name
    FROM     table t
    GROUP BY t.id;
    --> Fehler bei t.name
    
    SELECT 
      t.id, 
      max(t.name)
    FROM     table t
    GROUP BY t.id;
    


  • Also ich frage mehrere Felder ab, nicht nur die id.
    Wie ist es denn, wenn ich die text LIKE '%bla%' Ausdrücke einmal nach dem SELECT habe und einmal hinterm WHERE. Macht die DB dann zweimal die gleiche Arbeit, oder wird das LIKE einmal geprüft und sowohl zum Addieren der erfüllten Bedingungen, als auch zum Prüfen hinterm WHERE benutzt?



  • @don_basto Ah, ok. Jetzt verstehe ich das. Von Mysql bin ich das so nicht gewohnt ^^

    @LeGaN Gute Frage, nächste Frage. Welche DB isses denn?

    mfg
    xXx



  • Entschuldigung, ich war die ganze Zeit davon überzeugt, dass ich schon geschrieben hätte,mit welcher DB ich grad arbeite. Naja, nochmal lesen hat mich schlauer gemacht.

    Also ich arbeite mit ner MySQL-DB.



  • Bei Mysql musst du den Einwand von don_basto nicht beachten. Da Mysql sich nicht an der SQL-Standard hält klappt das so wie von mir beschrieben.
    Und was die geschwindigkeit betrifft musst du glaube ich mal testen. Du hast ja zwei Möglichkeiten:
    entweder mit HAVING
    oder alle vergleiche noch einmal ins WHERE

    Hab keine antwort im Netz gefunden, ob Mysql solche Berechnungen zwischenspeichert. Und das geht etwas über meine bisherige Erfahrung hinaus 😉

    mfg
    xXx



  • Also das mit dem GROUP BY funktioniert wunderbar! Jetzt ist es genau so, wie ichs wollte! Vielen Dank!


Log in to reply