7

Utilizzo il database northwind per aggiornare le mie competenze SQL creando alcune query più o meno complesse. Purtroppo non sono riuscito a trovare una soluzione per il mio ultimo caso d'uso: "Ottieni la somma dei cinque ordini migliori per ogni categoria nell'anno 1997."SQL - Sottoquery in funzione aggregata

Le tabelle coinvolte sono:

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

Ho provato la seguente query

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 

Beh ... Si è scoperto che sottoquery non sono ammessi nelle funzioni di aggregazione. Ho letto altri post su questo problema ma non sono riuscito a trovare una soluzione per il mio caso d'uso specifico. Spero che tu possa darmi una mano ...

+0

Quale versione di RDBMS stai utilizzando? Anche la tua domanda non è corretta perché l'ordine può appartenere a più di una categoria. –

+0

Sto utilizzando MSSQL 2012. Se si fa riferimento alla sottoquery, esiste una clausola WHERE - 'WHERE c.categoryID = p2.CategoryID' - che dovrebbe filtrare solo per una categoria. – Thomas

risposta

20

Le sottoquery non sono generalmente consentite in funzioni aggregate. Invece, sposta l'aggregato all'interno della sottoquery. In questo caso, avrete bisogno di un ulteriore livello di sottoquery a causa del 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

Grazie, ha fatto il lavoro! Solo per informazioni ... il CategoryID deve essere raggruppato. L'ultima riga dovrebbe apparire come 'GROUP BY c.CategoryName, c.CategoryID'. Ma grazie per il tuo tempo, questa domanda mi sembra davvero strana. Hai bisogno di analizzarlo in profondità ora ... :) – Thomas

3

Sicuramente un problema di sub-query qui è un excellent article su questo (scritto originariamente per Access ma la sintassi è identica), anche orderdate = 1997 darà la data dell'ordine per il 1 gennaio 1997 ' - avete bisogno di datepart (year, orderdate) = 1997, una volta ottenute le (fino a cinque) righe restituite per ogni categoria potete quindi incapsulare le righe restituite e aggregarle

+0

Grazie per il link. Hai ragione ho modificato il mio post. Dovrebbe essere ofc 'YEAR (o.OrderDate) = '1997'' – Thomas

+0

Potresti fornire un esempio per" incapsulare le righe restituite e aggregarle "? – Thomas

+0

SELECT xA, xB, xC, SUM (xd) AS D FROM (Qualsiasi istruzione di selezione sql valida contenente le colonne a, b, c, d, e) x GROUP BY xd –

3

Usa CTE con ROW_NUMBER funzione di rango, invece di subquery eccessivo.

;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

Mi sono imbattuto in un problema molto simile con una subquery di Access in cui i record sono stati ordinati per data. Quando ho usato la funzione di aggregazione "Ultimo", ho trovato che passava attraverso tutte le sottoquery e recuperava l'ultima riga di dati dalla tabella di Access e non la query ordinata come previsto. Anche se avrei potuto riscrivere la query per utilizzare la funzione di aggregazione all'interno del primo set di parentesi (come suggerito in precedenza) ho trovato più facile salvare i risultati della query come una tabella nel database ordinata nell'ordine che volevo e quindi utilizzare "Ultimo "Funzione di aggregazione per recuperare i valori che volevo. In futuro eseguirò una query di aggiornamento per mantenere aggiornati i risultati. Non efficiente ma efficace.