J'utilise la base de données northwind pour actualiser mes compétences en SQL en créant des requêtes plus ou moins complexes. Malheureusement, je n'ai pas trouvé de solution à mon dernier cas d'utilisation: "Obtenez la somme des cinq plus grosses commandes pour toutes les catégories de l'année 1997."
Les tables impliquées sont:
Orders(OrderId, OrderDate)
Order Details(OrderId, ProductId, Quantity, UnitPrice)
Products(ProductId, CategoryId)
Categories(CategoryId, CategoryName)
J'ai essayé la requête suivante
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
Eh bien ... Il s'est avéré que les sous-requêtes ne sont pas autorisées dans les fonctions d'agrégation. J'ai lu d'autres articles sur ce problème mais je n'ai pas trouvé de solution à mon cas d'utilisation spécifique. J'espère que vous pourrez m'aider ...
Les sous-requêtes ne sont généralement pas autorisées dans les fonctions d'agrégation. Au lieu de cela, déplacez l'agrégat inside la sous-requête. Dans ce cas, vous aurez besoin d'un niveau supplémentaire de sous-requête en raison du 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
Utilisez CTE avec ROW_NUMBER fonction de classement au lieu d'une sous-requête excessive.
;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
C'est certainement un problème de sous-requête ici est un excellent article à ce sujet (écrit à l'origine pour Access mais la syntaxe est identique), orderdate = 1997 donnera la date de commande pour le 1 er janvier 1997 '- il vous faut datepart (année, orderdate) = 1997, une fois que vous avez renvoyé (jusqu'à cinq) lignes pour chaque catégorie, vous pouvez ensuite encapsuler les lignes renvoyées et les regrouper.
J'ai rencontré un problème très similaire avec une sous-requête Access où les enregistrements ont été triés par date. Lorsque j'ai utilisé la fonction d'agrégation "Last", j'ai constaté qu'elle passait dans toutes les sous-requêtes et récupérait la dernière ligne de données de la table Access, et non la requête triée comme prévu. Bien que j'aurais pu réécrire la requête pour utiliser la fonction d'agrégation dans le premier ensemble de parenthèses (comme cela avait été suggéré précédemment), il m'a été plus facile d'enregistrer les résultats de la requête sous forme de table dans la base de données triée dans l'ordre que je souhaitais, puis d'utiliser le " "fonction agrégée pour récupérer les valeurs que je voulais. J'exécuterai une requête de mise à jour à l'avenir pour garder les résultats à jour. Pas efficace mais efficace.