2013-05-01 25 views
7

Używam bazy danych Northwind do odświeżania moich umiejętności SQL poprzez tworzenie bardziej lub mniej złożonych zapytań. Niestety nie mogłem znaleźć rozwiązania dla mojego ostatniego przypadku użycia: "Zdobądź pięć największych zamówień dla każdej kategorii w roku 1997."SQL - Podzapytowanie w funkcji agregującej

Tabele zaangażowane są:

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

Próbowałem następujące zapytanie

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 

Cóż ... Okazało się, że podzapytania nie są dozwolone w funkcji agregujących. Czytałem inne posty na ten temat, ale nie mogłem znaleźć rozwiązania dla mojego konkretnego przypadku użycia. Mam nadzieję, że możesz mi pomóc ...

+0

Z której wersji RDBMS korzystasz? Również twoje pytanie nie jest poprawne, ponieważ zamówienie może należeć do więcej niż jednej kategorii. –

+0

Używam MSSQL 2012. Jeśli odwołujesz się do podzapytania, istnieje klauzula WHERE - "WHERE c.categoryID = p2.CategoryID" - która powinna filtrować tylko dla jednej kategorii. – Thomas

Odpowiedz

20

Podzapytanie nie jest ogólnie dozwolone w funkcjach zagregowanych. Zamiast tego przenieś agregację wewnątrz podkwerendy. W tym przypadku trzeba dodatkowy poziom podzapytaniu powodu 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

Dzięki, że wykonali zadanie! Tylko dla informacji ... Identyfikator kategorii również musi być zgrupowany. Ostatni wiersz powinien wyglądać jak "GROUP BY c.CategoryName, c.CategoryID". Ale dzięki za poświęcony czas, to pytanie wygląda dla mnie naprawdę dziwnie. Trzeba to teraz dogłębnie przeanalizować ... :) – Thomas

3

Z pewnością jest to problem z sub-zapytaniem: tutaj jest excellent article (pierwotnie napisany dla Access, ale składnia jest identyczna), również orderdate = 1997 ustali datę zamówienia na 1 stycznia 1997 r. - potrzebujesz datepart (year, orderdate) = 1997, gdy masz już (do pięciu) wierszy zwróconych dla każdej kategorii, możesz zamknąć enkapsulację zwróconych wierszy i agregować je

+0

Dzięki za link. Masz rację, zredagowałem mój post. Powinien to być ofc 'YEAR (o.OrderDate) = '1997'' – Thomas

+0

Czy możesz podać przykład dla" enkapsulacji wierszy zwróconych i ich agregacji "? – Thomas

+0

SELECT xA, xB, xC, SUM (xd) AS D FROM (Dowolne poprawne polecenie sql select zawierające kolumny a, b, c, d, e) x GROUP BY xd –

3

Korzystając CTE z ROW_NUMBER rankingowej funkcji zamiast nadmiernego podzapytania.

;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

Wystąpił bardzo podobny problem z podzapytaniem programu Access, w którym rekordy zostały posortowane według daty. Kiedy użyłem funkcji agregującej "Ostatni", stwierdziłem, że przeszło ono przez wszystkie podzapytania i pobrało ostatni wiersz danych z tabeli Access, a nie posortowane zapytanie zgodnie z zamierzeniem. Chociaż mogłem przepisać zapytanie, aby użyć funkcji agregującej w obrębie pierwszego zestawu nawiasów (jak wcześniej sugerowano), łatwiej było zapisać wyniki zapytania jako tabelę w bazie danych posortowaną w kolejności, w jakiej chciałem, a następnie użyć "Ostatnia "funkcja agregująca do pobierania żądanych wartości. Uruchomę zapytanie o aktualizację w przyszłości, aby zachować bieżące wyniki. Nie skuteczny, ale skuteczny.

Powiązane problemy