2013-05-01 25 views
7

Ich verwende die Northwind-Datenbank, um meine SQL-Kenntnisse zu aktualisieren, indem ich einige mehr oder weniger komplexe Abfragen erstelle. Leider konnte ich für meinen letzten Anwendungsfall keine Lösung finden: "Holen Sie sich die Summe der fünf größten Aufträge für jede Kategorie im Jahr 1997."SQL - Unterabfrage in Aggregatfunktion

Die Tabellen beteiligt sind:

Orders(OrderId, OrderDate) 
Order Details(OrderId, ProductId, Quantity, UnitPrice) 
Products(ProductId, CategoryId) 
Categories(CategoryId, CategoryName) 

ich die folgende Abfrage

SELECT c.CategoryName, SUM(
    (SELECT TOP 5 od2.UnitPrice*od2.Quantity 
    FROM [Order Details] od2, Products p2 
    WHERE od2.ProductID = p2.ProductID 
    AND c.CategoryID = p2.CategoryID 
    ORDER BY 1 DESC)) 
FROM [Order Details] od, Products p, Categories c, Orders o 
WHERE od.ProductID = p. ProductID 
AND p.CategoryID = c.CategoryID 
AND od.OrderID = o.OrderID 
AND YEAR(o.OrderDate) = 1997 
GROUP BY c.CategoryName 

Nun versucht haben ... Es stellte sich heraus, dass Unterabfragen sind in Aggregatfunktionen nicht erlaubt. Ich habe andere Beiträge zu diesem Problem gelesen, konnte jedoch keine Lösung für meinen speziellen Anwendungsfall finden. Hoffe, dass Sie mir helfen können ...

+0

Welche Version von RDBMS verwenden Sie? Auch Ihre Frage ist nicht korrekt, da die Bestellung in mehr als einer Kategorie enthalten sein kann. –

+0

Ich verwende MSSQL 2012. Wenn Sie auf die Unterabfrage verweisen, gibt es eine WHERE-Klausel - "WHERE c.categoryID = p2.CategoryID" - die nur nach einer Kategorie filtern soll. – Thomas

Antwort

20

Unterabfragen sind in Aggregatfunktionen generell nicht erlaubt. Verschieben Sie stattdessen das Aggregat in die Unterabfrage. In diesem Fall erhalten Sie eine zusätzliche Ebene der Unterabfrage müssen wegen der top 5:

SELECT c.CategoryName, 
    (select sum(val) 
    from (SELECT TOP 5 od2.UnitPrice*od2.Quantity as val 
     FROM [Order Details] od2, Products p2 
     WHERE od2.ProductID = p2.ProductID 
     AND c.CategoryID = p2.CategoryID 
     ORDER BY 1 DESC 
     ) t 
) 
FROM [Order Details] od, Products p, Categories c, Orders o 
WHERE od.ProductID = p. ProductID 
AND p.CategoryID = c.CategoryID 
AND od.OrderID = o.OrderID 
AND YEAR(o.OrderDate) = 1997 
GROUP BY c.CategoryName, c.CategoryId 
+0

Danke das hat den Job gemacht! Nur für Info ... muss die CategoryID ebenfalls gruppiert werden. Die letzte Zeile sollte wie folgt aussehen: GROUP BY c.CategoryName, c.CategoryID. Aber danke für deine Zeit, diese Frage sieht für mich wirklich komisch aus. Müssen Sie jetzt gründlich analysieren ... :) – Thomas

3

Es ist definitiv ein Sub-Abfrage-Problem hier ist eine excellent article auf diese (ursprünglich für Access geschrieben, aber die Syntax ist identisch), auch orderdate = 1997 geben Bestelldatum für 1 Jan 1997 ' - Sie benötigen datepart (year, orderdate) = 1997, sobald Sie die (bis zu fünf) Zeilen für jede Kategorie zurückgegeben haben, können Sie die zurückgegebenen Zeilen kapseln und sie zusammenführen

+0

Danke für den Link. Du hast Recht, ich habe meinen Beitrag bearbeitet. Es sollte von 'YEAR (o.OrderDate) = '1997' ' – Thomas

+0

Könnten Sie ein Beispiel für" die zurückgegebenen Zeilen kapseln und aggregieren sie "? – Thomas

+0

SELECT xA, xB, xC, SUMME (xd) AS D FROM (Jede gültige sql SELECT-Anweisung enthält Spalten a, b, c, d, e) x GROUP BY xd –

3

Verwenden CTE mit ROW_NUMBER Ranking-Funktion anstelle von übermäßiger Unterabfrage.

;WITH cte AS 
(
    SELECT c.CategoryName, od2.UnitPrice, od2.Quantity, 
     ROW_NUMBER() OVER(PARTITION BY c.CategoryName ORDER BY od2.UnitPrice * od2.Quantity DESC) AS rn 
    FROM [Order Details] od JOIN Products p ON od.ProductID = p.ProductID 
          JOIN Categories c ON p.CategoryID = c.CategoryID 
          JOIN Orders o ON od.OrderID = o.OrderID 
    WHERE o.OrderDate >= DATEADD(YEAR, DATEDIFF(YEAR, 0, '19970101'), 0) 
    AND o.OrderDate < DATEADD(YEAR, DATEDIFF(YEAR, 0, '19970101')+1, 0) 
) 
    SELECT CategoryName, SUM(UnitPrice * Quantity) AS val 
    FROM cte 
    WHERE rn < 6 
    GROUP BY CategoryName 
0

Ich lief in ein sehr ähnliches Problem mit einer Access-Unterabfrage, wo die Datensätze nach Datum sortiert wurden. Als ich die "Last" -Aggregatfunktion verwendet habe, habe ich festgestellt, dass sie alle Unterabfragen durchläuft und die letzte Datenzeile aus der Access-Tabelle und nicht die sortierte Abfrage wie beabsichtigt abgerufen hat. Obwohl ich die Abfrage so umgeschrieben haben könnte, dass sie die Aggregatfunktion innerhalb des ersten Klammersatzes verwendet (wie zuvor vorgeschlagen), war es für mich einfacher, die Abfrageergebnisse als Tabelle in der Datenbank in der gewünschten Reihenfolge zu speichern und dann "Last" zu verwenden "Aggregatfunktion, um die gewünschten Werte zu erhalten. Ich werde in Zukunft eine Aktualisierungsabfrage ausführen, um die Ergebnisse aktuell zu halten. Nicht effizient, aber effektiv.

Verwandte Themen