Timo: MySQL: Schnittmengen abfragen

Hallo zusammen.

Ich verzweifel gerade an einer möglichst eleganten Filterabfrage der folgenden Tabelle

Artikelabfrage

SELECT artikelID FROM artikel WHERE kategorie=1

(1234, 2234, 3334 …)

Tabelle Filter

artikelID      filterValue

1234           2                     
1234           3
1234           4
2234           2
2234           3
2234           6
9222           2
...

Folgendes Problem: Ich habe eine Gruppe von ArtikelIDs abgefragt (1234, 2234, 3334 …) die ich anhand der Filter weiter eingrenzen oder ausweiten möchte. So würde meine gewüschte Abfrage aussehen:

Gib mir die ArtikelIDs aus meiner Liste, die (filterValue=2 OR filterValue=3) AND (filterValue=4) haben.

Wie mache ich das am effektivsten? Danke schon mal und liebe Grüße Timo

  1. Hallo,

    Gib mir die ArtikelIDs aus meiner Liste, die (filterValue=2 OR filterValue=3) AND (filterValue=4) haben.

    entweder hast du einen Denkfehler oder deine Logik ist falsch formuliert.
    filterValue kann nicht 2 oder 3 und gleichzeitig 4 sein.
    Was also meinst du wirklich?

    Einen schönen Tag noch
     Martin

    --
    Wie man sich bettet, so schallt es heraus.
    1. Hallo Martin,

      den Denkfehler hast Du - so wie ich zuerst auch.

      Guck Dir ArtikelID 1234 an. Die kommt dreimal vor, mit unterschiedlichen FilterValues, und würde damit die Bedingung erfüllen.

      Rolf

      --
      sumpsi - posui - obstruxi
    2. Hallo,

      danke für die Antwort. Stimmt, die Formulierung war ein wenig schwammig.

      Vielleicht mache ich es etwas konkreter:

      Ich brauche aus meiner Liste mit ArtikelIds (alle Jacken arikelID 2222,3334,333) alle roten (valueID = 2) und grünen (valueID = 3) in der Größe M (valueID = 4 ).

      1. Hallo Timo,

        Ich brauche aus meiner Liste mit ArtikelIds (alle Jacken arikelID 2222,3334,333) alle roten (valueID = 2) und grünen (valueID = 3) in der Größe M (valueID = 4 ).

        Das hat es nicht klarer gemacht. Eher konfuser. Aus dem "Oder" ist auf einmal ein "Und" geworden, und die IDs sind nicht die aus deiner ersten Beispieltabelle. Wie soll man daraus schlau werden?

        Ich hoffe, dass mein anderes Posting zu INTERSECT Dir weiterhilft.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo,

          Aus dem "Oder" ist auf einmal ein "Und" geworden,

          Das ist ja oft so, dass ein sprachliches „Und“ mit einem logischen „Or“ umgesetzt werden muss.

          Mich irritiert, dass in der Tabelle offenbar ValueID mal für Farbe und mal für Größe steht...

          Gruß
          Kalk

          1. Hallo Tabellenkalk,

            Mich irritiert, dass in der Tabelle offenbar ValueID mal für Farbe und mal für Größe steht...

            Dazu hatte ich schon eine Menge geschrieben - im Sinne von "was für ein Mülldatenmodell ist denn das?!" - aber ich denke, dass die hier gegebenen Beispiele fachfremd konstruiert sind (um keine Interna preiszugeben) und mit dem echten Problem nichts zu tun haben. Deswegen dürften diese Rückschlüsse irreführend sein und ich habe meinen Beitrag zur self-typischen Backgrounddiskussion wieder entfernt.

            Ich gehe daher davon aus, dass wir den tatsächlichen Entitätstyp nicht kennen und das Beispiel eher verwirrt als hilft. Das formale Problem lautet für mich:

            Es gibt eine Tabelle mit den Spalten id und value. Die Tabelle ist über der id-Spalte nicht UNIQUE, sondern es gibt mehrere Sätze zu einer ID, und damit gibt es zu jeder ID eine bestimmte Menge an Values. Gesucht sind nun diejenigen IDs, wo diese Value-Menge eine bestimmte Eigenschaft hat: (enthält 2 oder 3) UND (enthält 4).

            Natürlich kann ich falsch liegen und wir haben es tatsächlich mit den Unfallfolgen einer misslungenen Datenmodellierung zu tun. In diesem Fall bräuchten wir ein sehr viel genaueres Beispiel.

            Rolf

            --
            sumpsi - posui - obstruxi
  2. Hallo Timo,

    die Standardlösung wäre ein INTERSECT - aber den kennt MySQL nicht. Man kann ihn aber mit einem Subselect oder einem Join nachbauen.

    Welche Lösung genau passt, hängt von der übrigen Aufgabe ab. Geht es Dir nur um die Artikel-ID?

    Möglichkeit 1: Subselect. Sofern ein Artikel nur eine Row mit filterValue=4 hat, könnte sie am effizientesten sein.

    SELECT a1.artikelID
    FROM artikel a1
    WHERE a1.kategorie = 1
      AND a1.filterValue = 4
      AND EXISTS (SELECT * 
                  FROM artikel a2 
                  WHERE a2.artikelId = a1.artikelId 
                    AND a2.kategorie = 1
                    AND filterValue IN (2, 3)
    

    Möglichkeit 2: Inner Join. Würde in deinem Beispiel für artikelID=1234 zwei Zeilen liefern, darum muss ein DISTINCT dazu. Man müsste EXPLAINs laufen lassen, aber ich würde annehmen, dass diese Variante weniger performant ist. Kommt drauf an, wie gut der SQL Optimizer in deiner DB ist.

    SELECT DISTINCT artikelId
    FROM artikel a1 JOIN artikel a2
         ON a1.artikelId=a2.artikelId
         AND a1.kategorie=1
         AND a1.filterValue=4
         AND a2.kategorie=1
         AND a2.filterValue=2 IN (2, 3)
    

    Für beide Queries gilt: Ob bei a2 ebenfalls die Kategorie abgefragt werden muss, hängt von deinen Daten ab. Ist die Kategorie ein Oberbegriff zu den Artikeln, so dass alle Rows einer ArtikelID die gleiche Kategorie haben, oder kann es innerhalb einer ArtikelId Rows mit unterschiedlichen Kategorien geben?

    In beiden Fällen ist zu empfehlen, dass es einen Index gibt, der ArtikelId und FilterValue enthält. Damit lässt sich die Query durch Indexabfragen ausführen und es ist kein Zugriff auf den Data-Teil der Table nötig.

    Rolf

    --
    sumpsi - posui - obstruxi