Encoder: knifflige Abfrage

Hallo ihr

Ich hätte da etwas zum überlegen. Nehmen wir mal folgendes
SELECT * FROM Kunden INNER JOIN Aufträge ON Kunden.kundenID = Aufträge.kundenID

Davon sollen jetzt pro Kunde nur jeweils die letzten x Einträge ausgegeben werden.

Ein bisschen weiter runtergebrochen heißt das, ich suche in der Tabelle "Aufträge" pro Kunden-ID die letzten x Einträge.
Ich komm da allerdings nicht weiter. Oder um genauer zu sein, ich hab noch nicht mal eine Idee wie ich anfangen könnte.
Für einen einzelnen Kunden wärs kein Problem, aber für alle auf einmal? GROUP BY und was mir sonst einfällt, ist ja alles nicht geeignet.
Hat jemand ne Idee?

  1. Moin,

    Ich hätte da etwas zum überlegen. Nehmen wir mal folgendes
    SELECT * FROM Kunden INNER JOIN Aufträge ON Kunden.kundenID = Aufträge.kundenID

    Davon sollen jetzt pro Kunde nur jeweils die letzten x Einträge ausgegeben werden.

    Ich bin kein Experte, aber der INNER JOIN müsste quasi geLIMITed werden... Hilft dir das?

    Grüße Marco

    1. Hi!

      Davon sollen jetzt pro Kunde nur jeweils die letzten x Einträge ausgegeben werden.
      Ich bin kein Experte, aber der INNER JOIN müsste quasi geLIMITed werden... Hilft dir das?

      LIMIT wirkt auf gesamte Querys. Hier wird aber eine Limitierung innerhalb einer Gruppe benötigt. Gruppierungen aber können nur zum Erstellen von Aggregat-Werten über diese Gruppe verwendet werden. Auch ein GROUP_CONCAT(), das mehrere Werte einer Gruppe liefern kann, hat keine LIMIT-Klausel. Das Problem auf der verlinkten Seite ist auch kein vergleichbares, denn dort wird nur ein Wert benötigt, was mit einer korrelierten Unterabfrage problemlos möglich ist. Hier aber werden mehr als einer benötigt, was damit nicht mehr geht. Prinzipiell wären korrelierte Unterabfragen der Weg zur Lösung, denn nur die können ohne zu gruppieren sich auf ein Kriterium eines Datensatzes der Hauptabfrage einschränken und auch noch ein LIMIT verwenden. Allerdings kann man eine Subquery, die mehrere Datensätze liefert, nur in der FROM-Klausel unterbringen. Dort muss sie aber selbständig sein, eine Korrelation kann man nicht aufbauen.

      Ich denke nicht, dass das Problem mit einer Abfrage lösbar ist. Es lässt sich aber sicher eine Stored Procedure erstellen, in der man die benötigten Einzelschritte abarbeiten kann.

      • alle relevanten Kunden ermitteln, wobei ich grad nicht weiß, ob ein Cursor reicht oder auch eine tempräre Tabelle angelegt werden muss
      • eine temporäre Tabelle für die Auftragsdaten erstellen
      • über diese Menge iterieren und
          - für jede Kunden-ID eine Auftragsabfrage ausführen, inklusive der Limitierung
          - deren Ergebnisse zur temporären Auftragstabelle hinzufügen
      • die Kundendaten mit den Daten der temp. Auftragstabelle joinen

      Lo!

      1. Moin,

        Hi!
        Allerdings kann man eine Subquery, die mehrere Datensätze liefert, nur in der FROM-Klausel unterbringen. Dort muss sie aber selbständig sein, eine Korrelation kann man nicht aufbauen.

        Achso... Das war mir nicht bewusst, hab damit noch nicht gearbeitet...

        Ich denke nicht, dass das Problem mit einer Abfrage lösbar ist. Es lässt sich aber sicher eine Stored Procedure erstellen, in der man die benötigten Einzelschritte abarbeiten kann.

        Wenn es nicht um Millionen Datensätze geht, kann man doch auch einfach alles abfragen und in PHP in ein Array packen und dann das Array "stutzen", oder? Kommt allerdings auf den Umfang der Kundendatenbank an...

        Grüße Marco

        1. Wenn es nicht um Millionen Datensätze geht, kann man doch auch einfach alles abfragen und in PHP in ein Array packen und dann das Array "stutzen", oder? Kommt allerdings auf den Umfang der Kundendatenbank an...

          Wenn man sowieso schon PHP verwendet kann man auch zunächst alle Kunden auslesen, und in einer Schleife über alle Kunden ein Query mittels UNION so zusammensetzen, dass immer je Kunde die neuesten X Aufträge ausgewählt werden.
          Diese Vorgehensweise ist natürlich auch mit einer Stored Procedure möglich.

          1. Wenn man sowieso schon PHP verwendet kann man auch zunächst alle Kunden auslesen, und in einer Schleife über alle Kunden ein Query mittels UNION so zusammensetzen, dass immer je Kunde die neuesten X Aufträge ausgewählt werden.
            Diese Vorgehensweise ist natürlich auch mit einer Stored Procedure möglich.

            Dann würden aber bei x Kunden auch x SQL-Querys abgearbeitet, wenn ich das richtig verstehe. Ich würde das so machen wie der Threadsteller es geschrieben hat, und dann im PHP per Schleife die älteren Aufträge rauskegeln...
            Das würde mir performater scheinen. Oder läuft das bei einem INNER JOIN bei SQL sowieso so ab?

            Grüße Marco

            1. Hi!

              Dann würden aber bei x Kunden auch x SQL-Querys abgearbeitet, wenn ich das richtig verstehe. Ich würde das so machen wie der Threadsteller es geschrieben hat, und dann im PHP per Schleife die älteren Aufträge rauskegeln...

              Das ist nur sinnvoll/performant, wenn sich die Anzahl der Aufträge in Grenzen hält.

              Das würde mir performater scheinen. Oder läuft das bei einem INNER JOIN bei SQL sowieso so ab?

              Ein Inner Join verbindet nur alles linke mit allem rechten, unter Berücksichtigung der Join-Bedingung. Da wird nur das rausgekegelt, was keinen Partner findet. Eine Einschränkung nach einem Gruppenkriterium findet da nicht statt. Und es gibt auch keins, das den Fall des OP lösen könnte.

              Lo!

              1. Moin,

                Das ist nur sinnvoll/performant, wenn sich die Anzahl der Aufträge in Grenzen hält.

                Ja, das ist die Bedingung...

                Das würde mir performater scheinen. Oder läuft das bei einem INNER JOIN bei SQL sowieso so ab?
                Ein Inner Join verbindet nur alles linke mit allem rechten, unter Berücksichtigung der Join-Bedingung. Da wird nur das rausgekegelt, was keinen Partner findet. Eine Einschränkung nach einem Gruppenkriterium findet da nicht statt. Und es gibt auch keins, das den Fall des OP lösen könnte.

                Ich meinte eigentlich UNION :) Sry.

                Grüße Marco

            2. Dann würden aber bei x Kunden auch x SQL-Querys abgearbeitet, wenn ich das richtig verstehe. Ich würde das so machen wie der Threadsteller es geschrieben hat, und dann im PHP per Schleife die älteren Aufträge rauskegeln...

              Nein, dann würden bei x Kunden genau 2 Queries abgearbeitet. Das erste holt alle Kunden. Das zweite wird dynamisch zusammengestzt, d.h.: hole für Kunde 1 die aktuellsten Aufträge UNION hole für Kunde 2 die aktuellsten Aufträge UNION ...

  2. Hi,

    geht das nicht mit SubQuery?

    SELECT * FROM Kunden INNER JOIN Aufträge ON Kunden.kundenID = Aufträge.kundenID  
    WHERE Aufträge.auftragsID IN ( SELECT TOP 5 auftragsID FROM Aufträge WHERE kundenID = Kunden.kundenID ORDER BY auftragsDatum desc)
    

    Bei MySQL anstelle von TOP 5 eben Limit, bei Oracle rownum in where-Klausel.

    ~dave

    1. Hi!

      SELECT * FROM Kunden INNER JOIN Aufträge ON Kunden.kundenID = Aufträge.kundenID

      WHERE Aufträge.auftragsID IN ( SELECT TOP 5 auftragsID FROM Aufträge WHERE kundenID = Kunden.kundenID ORDER BY auftragsDatum desc)

        
      An diese Weise hab ich zwar nicht gedacht, aber das scheitert bei MySQL (in Version 5.0) daran, dass es LIMIT nicht in einer Subquery mag (jedenfalls nicht in dieser Form Subquery). Geht vielleicht in nachfolgende Versionen ...  
        
        
      Lo!
      
      1. Hi!

        SELECT * FROM Kunden INNER JOIN Aufträge ON Kunden.kundenID = Aufträge.kundenID

        WHERE Aufträge.auftragsID IN ( SELECT TOP 5 auftragsID FROM Aufträge WHERE kundenID = Kunden.kundenID ORDER BY auftragsDatum desc)

        
        > An diese Weise hab ich zwar nicht gedacht, aber das scheitert bei MySQL (in Version 5.0) daran, dass es LIMIT nicht in einer Subquery mag (jedenfalls nicht in dieser Form Subquery). Geht vielleicht in nachfolgende Versionen ...  
          
        Version 5.1 und 5.5 wollen das so auch nicht.  
          
          
        Lo!
        
    2. Also was korreliertes, wie man das ja nennt.
      Dass es so einfach sein könnte hätt ich echt nicht gedacht. Denkblockade...

      Danke fürs draufhelfen!