La clause SQL OVER () - quand et pourquoi est-elle utile?

169
    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 cet article et je ne comprends pas pourquoi j'en ai besoin. Que fait la fonction Over? Que fait Partitioning By-on? Pourquoi ne puis-je pas faire une requête avec l'écriture Group By SalesOrderID?

AvecVolantCouleurs
la source
30
Quel que soit le SGBDR que vous utilisez, le didacticiel Postgres peut être utile. A des exemples; m'a aidé.
Andrew Lazarus

Réponses:

144

Vous pouvez utiliser GROUP BY SalesOrderID. La différence est qu'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égation fenêtrées au lieu de GROUP BY, vous pouvez récupérer des valeurs agrégées et non agrégées. Autrement dit, bien que vous ne le fassiez pas dans votre exemple de requête, vous pouvez récupérer à la fois les OrderQtyvaleurs individuelles et leurs sommes, nombres, moyennes, etc. sur des groupes de mêmes SalesOrderIDs.

Voici un exemple pratique des raisons pour lesquelles les agrégats fenêtrés sont excellents. Supposons que vous deviez calculer le pourcentage d'un total de chaque valeur. Sans agrégats fenêtrés, vous devez d'abord dériver une liste de valeurs agrégées, puis la joindre à l'ensemble de lignes d'origine, c'est-à-dire comme ceci:

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?

Andriy M
la source
68

La OVERclause est puissante dans la mesure où vous pouvez avoir des agrégats sur différentes plages ("fenêtrage"), que vous utilisiez GROUP BYou non un

Exemple: obtenir le nombre par SalesOrderIDet 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 des COUNTs différents , nonGROUP 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)
gbn
la source
47

Si vous vouliez uniquement GROUP BY le SalesOrderID, vous ne pourrez pas inclure les colonnes ProductID et OrderQty dans la clause SELECT.

La clause PARTITION BY vous permet de diviser vos fonctions d'agrégation. Un exemple évident et utile serait si vous vouliez générer des numéros de ligne pour les lignes de commande sur 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 peut être légèrement décalée)

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
Tom H
la source
42

Laissez-moi vous expliquer avec un exemple et vous pourrez voir comment cela fonctionne.

En supposant que vous disposez de la table DIM_EQUIPMENT suivante:

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

Voyez ce qui s'est passé.

Vous pouvez compter sans Group By sur YEAR et Match with ROW.

Une autre façon intéressante d'obtenir le même résultat si, comme ci-dessous, en utilisant la clause WITH, WITH fonctionne comme une vue en ligne et peut simplifier la requête en particulier les requêtes complexes, ce qui n'est pas le cas ici car j'essaie juste 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;
Sanjay Singh
la source
17

La clause OVER, lorsqu'elle est combinée avec 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 en ligne.

OVER (PARTITION BY SalesOrderID) indique que pour la fonction SUM, AVG, etc ..., renvoie la valeur OVER un sous-ensemble des enregistrements renvoyés par la requête et PARTITION ce sous-ensemble BY la clé étrangère SalesOrderID.

Nous allons donc SOMMAIRE chaque enregistrement OrderQty pour CHAQUE SalesOrderID UNIQUE, et ce nom de colonne s'appellera «Total».

C'est un moyen BEAUCOUP plus efficace que d'utiliser plusieurs vues en ligne pour trouver 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
maple_shaft
la source
2
  • Aussi appelé Query Petitionclause.
  • Similaire à la Group Byclause

    • diviser les données en morceaux (ou partitions)
    • séparé par des limites de partition
    • fonction fonctionne dans les partitions
    • réinitialisé lors du franchissement de la limite de séparation

Syntaxe:
function (...) OVER (PARTITION BY col1 col3, ...)

  • Les fonctions

    • Fonctions familières telles que COUNT(), SUM(), MIN(), MAX(), etc.
    • De nouvelles fonctions aussi bien (par exemple ROW_NUMBER(), RATION_TO_REOIRT(), etc.)


Plus d'informations avec l'exemple: http://msdn.microsoft.com/en-us/library/ms189461.aspx

Elshan
la source
-3
prkey   whatsthat               cash   
890    "abb                "   32  32
43     "abbz               "   2   34
4      "bttu               "   1   35
45     "gasstuff           "   2   37
545    "gasz               "   5   42
80009  "hoo                "   9   51
2321   "ibm                "   1   52
998    "krk                "   2   54
42     "kx-5010            "   2   56
32     "lto                "   4   60
543    "mp                 "   5   65
465    "multipower         "   2   67
455    "O.N.               "   1   68
7887   "prem               "   7   75
434    "puma               "   3   78
23     "retractble         "   3   81
242    "Trujillo's stuff   "   4   85

C'est le résultat d'une requête. La table utilisée comme source est la même sauf qu'elle n'a pas de dernière colonne. Cette colonne est une somme mobile de la troisième.

Requete:

SELECT prkey,whatsthat,cash,SUM(cash) over (order by whatsthat)
    FROM public.iuk order by whatsthat,prkey
    ;

(le tableau devient public.iuk)

sql version:  2012

C'est un peu plus de dbase (1986), je ne sais pas pourquoi plus de 25 ans ont été nécessaires pour le terminer.

Алексей Неудачин
la source