USE AdventureWorks2008R2;
GO
SELECT SalesOrderID, ProductID, OrderQty
,SUM(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Total'
,AVG(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Avg'
,COUNT(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Count'
,MIN(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Min'
,MAX(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Max'
FROM Sales.SalesOrderDetail
WHERE SalesOrderID IN(43659,43664);
J'ai lu sur cet article et je ne comprends pas pourquoi j'en ai besoin. Que fait la fonction Over
? Que fait Partitioning By
? Pourquoi ne puis-je pas faire une requête en écrivant Group By SalesOrderID
?
Vous pouvez utiliser GROUP BY SalesOrderID
. La différence est que, avec GROUP BY, vous ne pouvez avoir que les valeurs agrégées pour les colonnes qui ne sont pas incluses dans GROUP BY.
En revanche, en utilisant des fonctions d'agrégat fenêtrées au lieu de GROUP BY, vous pouvez récupérer des valeurs agrégées et non agrégées. C'est-à-dire que, bien que vous ne fassiez pas cela dans votre exemple de requête, vous pouvez récupérer les deux valeurs OrderQty
individuelles, ainsi que leurs sommes, comptes, moyennes, etc. sur des groupes de même SalesOrderID
s.
Voici un exemple pratique de la grande qualité des agrégats fenêtrés. Supposons que vous deviez calculer le pourcentage d'un total de chaque valeur. Sans les agrégats fenêtrés, vous devez d’abord dériver une liste de valeurs agrégées, puis la renvoyer à l’ensemble de lignes original, comme suit:
SELECT
orig.[Partition],
orig.Value,
orig.Value * 100.0 / agg.TotalValue AS ValuePercent
FROM OriginalRowset orig
INNER JOIN (
SELECT
[Partition],
SUM(Value) AS TotalValue
FROM OriginalRowset
GROUP BY [Partition]
) agg ON orig.[Partition] = agg.[Partition]
Maintenant, regardez comment vous pouvez faire la même chose avec un agrégat fenêtré:
SELECT
[Partition],
Value,
Value * 100.0 / SUM(Value) OVER (PARTITION BY [Partition]) AS ValuePercent
FROM OriginalRowset orig
Beaucoup plus facile et plus propre, n'est-ce pas?
La clause OVER
est puissante en ce que vous pouvez avoir des agrégats sur différentes plages ("fenêtrage"), que vous utilisiez un GROUP BY
ou non.
Exemple: obtenir le nombre par SalesOrderID
et le nombre de tous
SELECT
SalesOrderID, ProductID, OrderQty
,COUNT(OrderQty) AS 'Count'
,COUNT(*) OVER () AS 'CountAll'
FROM Sales.SalesOrderDetail
WHERE
SalesOrderID IN(43659,43664)
GROUP BY
SalesOrderID, ProductID, OrderQty
Obtenez différent COUNT
s, no GROUP BY
SELECT
SalesOrderID, ProductID, OrderQty
,COUNT(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'CountQtyPerOrder'
,COUNT(OrderQty) OVER(PARTITION BY ProductID) AS 'CountQtyPerProduct',
,COUNT(*) OVER () AS 'CountAllAgain'
FROM Sales.SalesOrderDetail
WHERE
SalesOrderID IN(43659,43664)
Si vous vouliez uniquement GROUPER PAR le SalesOrderID, vous ne pourriez pas inclure les colonnes ProductID et OrderQty dans la clause SELECT.
La clause PARTITION BY vous permet de diviser vos fonctions d'agrégat. Un exemple évident et utile serait de générer des numéros de ligne pour les lignes de commande d'une commande:
SELECT
O.order_id,
O.order_date,
ROW_NUMBER() OVER(PARTITION BY O.order_id) AS line_item_no,
OL.product_id
FROM
Orders O
INNER JOIN Order_Lines OL ON OL.order_id = O.order_id
(Ma syntaxe est peut-être légèrement incorrecte)
Vous obtiendrez alors quelque chose comme:
order_id order_date line_item_no product_id
-------- ---------- ------------ ----------
1 2011-05-02 1 5
1 2011-05-02 2 4
1 2011-05-02 3 7
2 2011-05-12 1 8
2 2011-05-12 2 1
Laissez-moi vous expliquer avec un exemple et vous pourrez voir comment cela fonctionne.
En supposant que vous ayez la table suivante DIM_EQUIPMENT:
VIN MAKE MODEL YEAR COLOR
-----------------------------------------
1234ASDF Ford Taurus 2008 White
1234JKLM Chevy Truck 2005 Green
5678ASDF Ford Mustang 2008 Yellow
Exécuter sous SQL
SELECT VIN,
MAKE,
MODEL,
YEAR,
COLOR ,
COUNT(*) OVER (PARTITION BY YEAR) AS COUNT2
FROM DIM_EQUIPMENT
Le résultat serait comme ci-dessous
VIN MAKE MODEL YEAR COLOR COUNT2
----------------------------------------------
1234JKLM Chevy Truck 2005 Green 1
5678ASDF Ford Mustang 2008 Yellow 2
1234ASDF Ford Taurus 2008 White 2
Voir ce qui s'est passé.
Vous pouvez compter sans Group By on YEAR et Match with ROW.
Une autre façon intéressante d’obtenir le même résultat si, comme indiqué ci-dessous, avec WITH Clause, WITH fonctionne comme une VUE en ligne et peut simplifier la requête, même si elle est complexe, ce qui n’est pas le cas ici même si j’essaie simplement de montrer l’utilisation
WITH EQ AS
( SELECT YEAR AS YEAR2, COUNT(*) AS COUNT2 FROM DIM_EQUIPMENT GROUP BY YEAR
)
SELECT VIN,
MAKE,
MODEL,
YEAR,
COLOR,
COUNT2
FROM DIM_EQUIPMENT,
EQ
WHERE EQ.YEAR2=DIM_EQUIPMENT.YEAR;
La clause OVER associée à PARTITION BY indique que l'appel de fonction précédent doit être effectué de manière analytique en évaluant les lignes renvoyées de la requête. Considérez-le comme une instruction GROUP BY intégrée.
OVER (PARTITION BY SalesOrderID)
indique que pour les fonctions SUM, AVG, etc.
Nous allons donc résumer chaque enregistrement OrderQty pour CHAQUE UNIQUE SalesOrderID, et le nom de cette colonne sera appelé "Total".
C'est un moyen BEAUCOUP plus efficace que d'utiliser plusieurs vues en ligne pour obtenir les mêmes informations. Vous pouvez alors placer cette requête dans une vue en ligne et filtrer sur Total.
SELECT ...,
FROM (your query) inlineview
WHERE Total < 200
Query Petition
Clause.Similaire à la clause Group By
Syntaxe:
fonction (...) OVER (PARTITION DE col1 col3, ...)
Les fonctions
COUNT()
, SUM()
, MIN()
, MAX()
, etc.ROW_NUMBER()
, RATION_TO_REOIRT()
, etc.)
Plus d'infos avec l'exemple: http://msdn.Microsoft.com/en-us/library/ms189461.aspx