WernerK: Select Ergebnisse aus Join einschränken

Hallo,

Ich habe eine Select Abfrage über mehrere Tabellen. In einer JOIN Tabelle (BestellTable) sind immer mehrere Positionen pro Bestellung drin. Als Ausgabe bekommt man daher auch alle Zeilen dieser ArtikelTable. Ich möchte jedoch gerne immer nur eine Zeile dieser gejointen Tabelle haben.

Start vereinfachte Abfrage:

SELECT 
A.Bestellnummer,
A.Lieferant,
B.Artikel;
B.Preis
FROM
BestellTable A
JOIN
ArtikelTable B ON A.ID = B.ID

Das Ergebnis wäre dann etwa:

Bestellnummer,Lieferant,Artikel,Preis

1 Meier Apfel 5

2 Müller Birne 3

2 Müller Apfel 2

4 Schulze Pflaume 4

Aussehen sollte das Ergebnis aber so: (also immer nur ein Datensatz aus Tabelle zwei)

Bestellnummer,Lieferant,Artikel,Preis

1 Meier Apfel 5

2 Müller Birne 3

4 Schulze Pflaume 4

Wie kann man dies so einschränken? Ich bin weder mit DISTINCT noch mit einem Subselect weitergekommen.

Gruss

Werner

  1. Hallo WernerK,

    schwierig.

    also immer nur ein Datensatz aus Tabelle zwei

    ist keine technisch brauchbare Formulierung.

    Also anders formuliert: Warum ist

    2 Müller Birne 3

    der richtige Satz? Warum nicht

    2 Müller Apfel 2

    Wenn Du das formulieren kannst, kommen wir vielleicht weiter.

    Rolf

    --
    sumpsi - posui - clusi
    1. Hallo Rolf,

      tja, genau diese Frage habe ich auch gestellt 😀

      Für mich macht das eigentlich auch keinen Sinn. Welcher Datensatz soll man anzeigen, welcher nicht?

      Belassen wir es einmal dabei: Wäre es technisch bzw. per SQL möglich?

      Gruss

      Werner

      1. Tach!

        Für mich macht das eigentlich auch keinen Sinn. Welcher Datensatz soll man anzeigen, welcher nicht?

        Nun, der Aufgabensteller wird sich vielleicht was gedacht haben, wenn er zur Bestellung nur einen Artikel sehen möchte. Aber was? Kannst du ihn nicht fragen? Eine Summe der Artikelpreise oder die Anzahl der Posten kann man sich ja noch ohne weitere Begründung als sinnvoll vorstellen.

        Belassen wir es einmal dabei: Wäre es technisch bzw. per SQL möglich?

        Es gibt technische Lösungen nur, wenn ein Algorithmus oder eine Regel definierbar ist. Ohne Klärung der obigen Frage geht es also nicht.

        Um es per Join realisieren zu können, muss du entweder die Join-Bedingung so formulieren, dass lediglich ein Datensatz entsteht, und dazu wissen, anhand welcher Merkmale dieser selektiert werden kann. Das müssen aber eigene Werte sein, keine Metawerte wie erster/letzter/jüngster von mehreren. Oder der Wert lässt sich per Correlated Subquery ermitteln.

        Eine Alternative wäre, wenn du es anhand der gejointen Menge schaffst, mittels Having die ungewünschten Datensätze herauszuwerfen. Das stelle ich mir aber schwer vor, weil man dafür ja die anderen Datensätze der Bestellung mit berücksichtigen muss, das Having aber nur auf einen schauen kann. Oder es kommt wieder eine Correlated Subquery zu Hilfe. Zu bevorzugen wäre aber die Join-Bedingungsvariante, weil dabei keine unnötig große Zwischenmenge entsteht.

        Eine weitere Möglichkeit, die mir einfällt, ist mehrstufig. Gruppiere die Artikel per Bestellung, finde den Wert, der den einen Datensatz genau identifiziert, den du pro Gruppe haben möchtest. Das muss für alle Gruppen erfolgen. Dann kannst du anhand dieser Werte ein Select formulieren mit WHERE id IN (deine ermittelte Liste) und diese Menge an die Bestellungen joinen. Es muss nicht IN() sein, aber irgendwie so nach diesem Prinzip.

        Wenn es um den ersten oder letzten einer sortieren Gruppe geht, denkst du vielleicht an sowas wie LIMIT bei MySQL oder TOP bei MS-SQL, aber das wirkt ja nur auf die gesamte (Teil-)Menge, nicht auf Gruppen, ist also nicht der Weg zum Ziel beim Gruppieren. Wohl aber in einer Correlated Subquery.

        Zu guter Letzt gibt es auch noch den Weg ohne Join allein mit einer Correlated Subquery in der SELECT-Klausel. Aber die kann an der Stelle nur einen einzigen Wert liefern und ist weniger geeignet, wenn mehrere Daten des Artikels in der Ergebnismenge enthalten sein sollen. Das müsste sonst je Wert eine eigene Subquery sein.

        Alles in allem steht oder fällt die Realisierbarkeit, wie schon gesagt, mit der Kenntnis des Auswahlkriteriums.

        dedlfix.

      2. Hallo WernerK,

        du hast diese Aufgabe also gestellt bekommen, und verstehst sie selbst nicht. Das ist schlecht. Weil - wenn Du es nicht verstehst und uns erklären kannst, wie soll man dann eine Lösung finden?

        Es GIBT durchaus SQL Formulierungen, mit denen man eine 1:n-Beziehung auf 1:1 reduzieren kann, es muss dann allerdings eine Regel existieren, die exakt einen Satz findet. Bei Müllers Apfel und Birne - tja. Du hast also Bestellungen, mit einer Bestellnummer. Ob eine Bestellung komplett bei einem Lieferanten erfolgt oder auch auf mehrere Lieferanten verteilt sein kann, das verrät uns dein Beispiel nicht. Nehmen wir also an. Eine bestimmte Bestellung erfolgt vollständig bei einem Lieferanten.

        Diese Bestellung umfasst dann mehrere Artikel. Nun kommt die Frage: Welchen Sinn hat es, eine Bestellung mit mehreren Artikeln auf einen Satz zu reduzieren? Entweder suche ich einen bestimmten Artikel, oder ich will die Bestellsumme wissen, oder mich interessieren die Artikel gar nicht. Wenn ich einen bestimmten Artikel suche, dann komme ich ohne Kriterien nicht weiter. Will ich den letzten im Alphabet, den mit dem höchsten Preis - beides trifft auf deine Birne zu.

        Die beiden anderen Fälle sind trivial, für die Bestellsumme lässt man den Artikelnamen weg, gruppiert über Bestellnummer und Lieferant und bildet SUM() über die Preise. Und wenn mich die Artikel nicht interessieren, dann joine ich sie nicht hinzu.

        Mal angenommen, ich möchte nur den Bestellposten mit dem Artikel haben, der den höchsten Preis hat. Das funktioniert allerdings nur dann, wenn diese Regel eindeutig ist, andernfalls habe ich doch wieder mehrere Sätze. Wie auch immer - man könnte das so machen:

        SELECT A.Bestellnummer, A.Lieferant, B.Artikel, B.Preis
        FROM BestellTable A JOIN ArtikelTable B ON A.ID = B.ID
        WHERE B.Preis = (SELECT MAX(Preis) FROM ArtikelTable C WHERE C.ID=B.ID)
        

        Rolf

        --
        sumpsi - posui - clusi