Z€ddix: Datenbankabfrage

Grüße,
folgende Datenbank-abfrage möchte ich möglichst sparend durchführen(Möglichst nur eine SQL Abfrage).
Zur Verfügung stehen PHP und MySQL.

Ich bekomme per PhP Folgenden Eingabestring gegeben: Haus/Bad/32/neu

In der Datenbank wäre dann zum Beispiel die Einträge:

Garten/Baum/Blatt
Garten/Pflanze/2/Blüte
Haus/Bad/?/neu
Haus/Bad/?/?
Haus/Bad/?
Haus/Wohnzimmer/?/alt
Haus/?/Fernseher/alt
Haus/?/32/neu

Nun möchte ich das (am liebsten mit einer Geschickt sortierten MySQL Abfrage), das der String möglichst passend (nach Ebenen sortiert) heraus gesucht wird. AM  liebsten mit LIMIT 1.

Wenn der String genau so vorgefunden wird sollte die Ausgabe sofort erfolgen. Falls der String nicht exakt in der Datenbank vorgefunden wird, sollen die Platzhalter (?) verwendet werden, immer so spezifisch wie möglich.

Haus/Bad/?/neu (best passender String)
Haus/Bad/?/?
Haus/Bad/?
Haus/?/32/neu
Haus/?/Fernseher/alt
Haus/Wohnzimmer/?/alt
Garten/Baum/Blatt
Garten/Pflanze/2/Blüte

Wie würdet ihr das Problem angehen? Wie sortieren?

Gruß Zeddix

  1. مرحبا

    Haus/Bad/?/neu (best passender String)
    Haus/Bad/?/?
    Haus/Bad/?
    Haus/?/32/neu
    Haus/?/Fernseher/alt
    Haus/Wohnzimmer/?/alt
    Garten/Baum/Blatt
    Garten/Pflanze/2/Blüte

    Wie würdet ihr das Problem angehen? Wie sortieren?

    Erst mal müsstest du beschreiben, was der Sinn der ganzen Sache sein soll. Sollen das Links zu Seiten sein?

    mfg

    1. Ja, im Endeffekt werden es Links zu Seiten werden, die dann halt auch Parameter enthalten. Zu diesen Seiten sind in der Datenbank halt bestimmte Sachen gespeichert, z.b. Module die dazu geladen werden.

      1. مرحبا

        Ja, im Endeffekt werden es Links zu Seiten werden, die dann halt auch Parameter enthalten. Zu diesen Seiten sind in der Datenbank halt bestimmte Sachen gespeichert, z.b. Module die dazu geladen werden.

        Am einfachsten wäre es, wenn du den String auseinander nimmst und für jeden Wert in der DB prüfst, ob es einen Inhalt gibt.

        Also aus

        Haus/Bad/32/neu

        alles zwischen den "/" auslesen und damit dein Select-Statement füttern.
        Das könntest du dann auch mit einer Abfrage lösen, in dem du OR verwendest.

        mfg

        1. Haus/Bad/32/neu

          alles zwischen den "/" auslesen und damit dein Select-Statement füttern.

          In MySQL könnte auch FIND_IN_SET() helfen - das ist auch nicht schneller, aber bequemer, verständlicher und leichter lesbar als irgend eine LIKE-Wüste mit vielen OR-Verknüpfungen.

        2. Ja, genau das habe ich bereits gemacht, allerdings kann es dann (Unter umständen) bei einem 10 Parameter langen String bis zu 10 MySQL abfragen geben, was selbst mit geschickten Chache zügig auf die Performance gehen kann.
          Kann man dies nicht geschickt in eine, dafür etwas größere Abfrage schieben?

          mfg

          1. Kann man dies nicht geschickt in eine, dafür etwas größere Abfrage schieben?

            Wenn die Dinger Pfade darstellen, musst du sie in erster Linie normalisieren - entweder in einer Eltern-Kind-Struktur oder in Nested Sets.

            Solange da wirklich in einem Feld foo/bar/baz steht, ist da nicht viel zu machen

            So z.B.

            id parent segemnt
            0         foo
            1  0      bar
            2  1      baz

          2. مرحبا

            Ja, genau das habe ich bereits gemacht, allerdings kann es dann (Unter umständen) bei einem 10 Parameter langen String bis zu 10 MySQL abfragen geben, was selbst mit geschickten Chache zügig auf die Performance gehen kann.
            Kann man dies nicht geschickt in eine, dafür etwas größere Abfrage schieben?

            Ich hab sowas ähnliches programmiert, ich befürchte aber, dass es alles andere als Performant ist.
            http://test.kum-bochum.de/

            Im Endeffekt wirst du nicht drumherum kommen, für jeden String mindestens einmal in der DB zu suchen.

            Oder du baust dir mit allen Teil-Strings ein Select-Statement, etwa

            SELECT wai  
            FROM  t  
            WHERE 'string' = $_URL[1]  
            OR    'string' = $_URL[2]  
            OR    'string' = $_URL[3]
            

            usw.

            mfg

            1. Oder du baust dir mit allen Teil-Strings ein Select-Statement, etwa

              SELECT wai

              FROM  t
              WHERE 'string' = $_URL[1]
              OR    'string' = $_URL[2]
              OR    'string' = $_URL[3]

                
              Davon ausgehend, dass "string" in "foo/bar/string/baz" gesucht werden soll, welches im Datenbank-Feld "URL" liegt:  
                
              ungetestet:  
              `FIND_IN_SET('string', REPLACE('/', ',', URL));`{:.language-sql}  
                
                
              
              
              1. مرحبا

                Davon ausgehend, dass "string" in "foo/bar/string/baz" gesucht werden soll, welches im Datenbank-Feld "URL" liegt:

                Will er ja garnicht, er will für jeden Stringteil was finden, also bei

                "foo/bar/string/baz"

                sollen im Ergebnis alle einzelnen Teilstrings berücksichtigt werden.

                foo
                bar
                string
                baz

                ungetestet:
                FIND_IN_SET('string', REPLACE('/', ',', URL));

                Hier müsste er erst wieder in einer Schleife jedes einzelne Wort prüfen; dass will er ja umgehen.

                mfg

                1. Hier müsste er erst wieder in einer Schleife jedes einzelne Wort prüfen; dass will er ja umgehen.

                  Ja dann dreht man die beiden Argumente von FIND_IN_SET halt um

                  schwierig wirds erst, wenn man die teile eines Pfades in einem anderen suchen will - wenn du das mit einer OR-Konstrukt machst, wirst du nicht mehr fertig - FIND_IN_SET hingegen verschachtelst du halt 2x - du kannst auch FIND_IN_SET als Argument für FIND_IN_SET verwenden - langsam, aber geht.

                  Zumal, wenn du nach "foo" in "foo/bar/baz/foobar findest - soll jetzt foo gefunden werden oder auch foobar? (like %foo%) wenn nicht muss du zuerst den Pfad mit concat hinten und vorne ergänzen zu /foo/bar/baz/foobar/ und dann nach like %/foo/% suchen - all das erledigt FIND_IN_SET aber schon.

                  Das Problem ist aber hier einfach die fehlende Normalisierung mit der man so einen Schmarrn gar nicht braucht.

                  1. مرحبا

                    ich habe OP so verstanden, dass er alle Einträge zurück haben will, die auf jeden einzelnen String matchen, also bei

                    /foo/bar/baz/foobar/

                    Soll in der DB nachgesehen werden für:

                    foo
                    bar
                    baz
                    foobar

                    Mit einem einfachen "OR" wäre das in einem Statement ganz leicht zu realisieren, wahlweise mit WHERE oder LIKE.

                    Kann find_in_set für jedes Wort einzeln schauen, oder müsste er jede abfrage Seperat starten?

                    mfg

                    1. Also ich werde mich ersteinmal kurz mit der Logik von FIND_IN_SET auseinanderesetzten, da ich die Funktion nicht gut kenne und mich da ersteinmal einarbeiten muss.
                      In der Zweischenzeit versuche ich das ganze noch ein wenig mehr zu präzisieren.
                      Ich möchte nicht für jeden einzelnen Teilstring in der DB nachschauen, sondern nur das "best match" finden, also den String in der DB der am nächsten dem Input String kommt. Dabei soll Ebenenweise vorgegangen werden:
                      Für
                      /foo/bar/baz/foobar/ gilt also

                      Suche alle Einträge mit foo
                       wenn gefunden fahre fort mit bar
                       wenn nicht, dann gucke ob ein Eintrag ? existiert
                        wenn gefunden, dann fahre fort mit bar
                        wenn nicht, dann gebe FALSE zurück

                      2te Ebene
                      Suche alle Einträge mit bar
                       wenn gefunden fahre fort mit baz
                       wenn nicht, dann gucke ob ein Eintrag ? existiert
                        wenn gefunden, dann fahre fort mit baz
                        wenn nicht, dann gebe Eintrag zurück

                      Ich hoffe das erklärt es besser.

                      1. Also ich werde mich ersteinmal kurz mit der Logik von FIND_IN_SET auseinanderesetzten, da ich die Funktion nicht gut kenne und mich da ersteinmal einarbeiten muss.

                        Das wird nicht funktionieren.

                        Am besten ist es hier wahrscheinlich, wenn du mit FIND_IN_SET eine Liste der möglichen Kandidaten ermittelst und diese dann in PHP abarbeitest.

                        1. Klingt nach einer guten Idee, ich werde mich heute Abend mal ransetzten  und evtl die nächsten Tage hier nochmal das Ergebnis posten.

                      2. مرحبا

                        Ich hoffe das erklärt es besser.

                        Du willst einfach nur die aufrufende Seite identifizieren. Vom Ansatz her war es schon richtig, nur in der durchführung hast du ein paar fehler gemacht.

                        Wenn die Seite aufgerufen wird

                        /foo/bar/foobar

                        musst du zuerst mit "foobar" suchen.
                        Wenn "foobar" Eltern hat, rückwärts die URL rekonstruieren, ausgehend von "foobar". Die Eltern müssen natürlich in der DB stehen.

                        Dein Resultat aus der DB könnte dann wie folgt aussehen (mehrere treffer auf "foobar")

                        foobar/bar/foo/
                        foobar/example/
                        foobar/anything/try/

                        Jetzt kannst du diese Strings mit der aufrufenden URL vergleichen und et voilá, du hast eine eindeutige ID, weil "foobar/bar/foo/" vorhanden ist.

                        Selbst wenn "foobar" doppelt in der DB steht, kann anhand der Eltern die Seite eindeutig idetifiziert werden.

                        mfg

                  2. مرحبا

                    Ja dann dreht man die beiden Argumente von FIND_IN_SET halt um

                    Ok, FIND_IN_SET ist auch geeignet ;)

                    Ich hatte zweifel, dass FIND_IN_SET ein array selbst durchläuft, weil bei mir URL ein array ist.
                    URL kann aber auch ein String sein, von daher genau richtig.

                    mfg

                    1. Ja dann dreht man die beiden Argumente von FIND_IN_SET halt um

                      Ok, FIND_IN_SET ist auch geeignet ;)

                      Nicht nur "auch geeignet" es ist genau für den Zweck gedacht, eine Kommaseparierte Liste an Werten gegen einen statischen String (oder einen Feldinhalt) zu prüfen - wenn das Trennzeichen kein Komma ist, muss man es halt Replacen

                      Ich gehe davon aus, dass in einem Pfad ohnehin kein Komma vorkommt, da es entsprechend kodiert sein müsste - von der Seite sehe ich eben kein Problem.