La cláusula SQL OVER (): ¿cuándo y por qué es útil?

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);

Leí sobre esa cláusula y no entiendo por qué la necesito. ¿Qué hace la función Over? ¿Qué Partitioning Byhacer? ¿Por qué no puedo hacer una consulta con la escritura Group By SalesOrderID?

Con gran éxito
fuente
30
No importa qué RDBMS use, el tutorial de Postgres puede ser útil. Tiene ejemplos; me ayudó.
Andrew Lazarus

Respuestas:

144

Usted puede utilizar GROUP BY SalesOrderID. La diferencia es que con GROUP BY solo puede tener los valores agregados para las columnas que no están incluidas en GROUP BY.

Por el contrario, utilizando funciones agregadas en ventana en lugar de GROUP BY, puede recuperar valores agregados y no agregados. Es decir, aunque no está haciendo eso en su consulta de ejemplo, puede recuperar OrderQtyvalores individuales y sus sumas, recuentos, promedios, etc. sobre grupos de los mismos SalesOrderIDs.

Aquí hay un ejemplo práctico de por qué los agregados con ventana son geniales. Suponga que necesita calcular qué porcentaje de un total es cada valor. Sin agregados en ventanas, primero tendría que derivar una lista de valores agregados y luego unirlos nuevamente al conjunto de filas original, es decir, así:

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]

Ahora mire cómo puede hacer lo mismo con un agregado con ventana:

SELECT
  [Partition],
  Value,
  Value * 100.0 / SUM(Value) OVER (PARTITION BY [Partition]) AS ValuePercent
FROM OriginalRowset orig

Mucho más fácil y limpio, ¿no?

Andriy M
fuente
68

La OVERcláusula es poderosa porque puede tener agregados en diferentes rangos ("ventanas"), ya sea que use GROUP BYo no

Ejemplo: obtener conteo por SalesOrderIDy conteo de todos

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

Obtener diferentes COUNTs, noGROUP 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
fuente
47

Si solo quisiera GRUPAR POR el SalesOrderID, entonces no podría incluir las columnas ProductID y OrderQty en la cláusula SELECT.

La cláusula PARTITION BY le permite dividir sus funciones agregadas. Un ejemplo obvio y útil sería si quisiera generar números de línea para líneas de pedido en un pedido:

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

(Mi sintaxis puede estar ligeramente apagada)

Luego obtendría algo como:

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
fuente
42

Déjame explicarte con un ejemplo y podrás ver cómo funciona.

Suponiendo que tiene la siguiente tabla DIM_EQUIPMENT:

VIN         MAKE    MODEL   YEAR    COLOR
-----------------------------------------
1234ASDF    Ford    Taurus  2008    White
1234JKLM    Chevy   Truck   2005    Green
5678ASDF    Ford    Mustang 2008    Yellow

Ejecutar debajo de SQL

SELECT VIN,
  MAKE,
  MODEL,
  YEAR,
  COLOR ,
  COUNT(*) OVER (PARTITION BY YEAR) AS COUNT2
FROM DIM_EQUIPMENT

El resultado sería el siguiente

VIN         MAKE    MODEL   YEAR    COLOR     COUNT2
 ----------------------------------------------  
1234JKLM    Chevy   Truck   2005    Green     1
5678ASDF    Ford    Mustang 2008    Yellow    2
1234ASDF    Ford    Taurus  2008    White     2

Mira lo que pasó.

Puede contar sin Agrupar por en AÑO y Emparejar con FILA.

Otra FORMA interesante para obtener el mismo resultado si, como se muestra a continuación, utiliza la cláusula WITH, WITH funciona como VISTA en línea y puede simplificar la consulta, especialmente las complejas, lo cual no es el caso aquí, ya que solo estoy tratando de mostrar el uso

 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
fuente
17

La cláusula OVER cuando se combina con PARTITION BY indica que la llamada a la función anterior debe realizarse analíticamente evaluando las filas devueltas de la consulta. Piense en ello como una declaración GROUP BY en línea.

OVER (PARTITION BY SalesOrderID) indica que para la función SUMA, AVG, etc., devuelve el valor SOBRE un subconjunto de los registros devueltos de la consulta y PARTICIONA ese subconjunto POR la ​​clave foránea SalesOrderID.

Por lo tanto, SUMAREMOS todos los registros de OrderQty para CADA ID de pedido único, y ese nombre de columna se llamará 'Total'.

Es un medio MUCHO más eficiente que usar múltiples vistas en línea para encontrar la misma información. Puede colocar esta consulta dentro de una vista en línea y luego filtrar en Total.

SELECT ...,
FROM (your query) inlineview
WHERE Total < 200
eje de arce
fuente
2
  • También llamado Query Petitioncláusula.
  • Similar a la Group ByCláusula

    • dividir los datos en trozos (o particiones)
    • separado por límites de partición
    • la función se realiza dentro de particiones
    • reinicializado al cruzar el límite de separación

Sintaxis:
función (...) SOBRE (PARTICIÓN POR col1 col3, ...)

  • Las funciones

    • Funciones conocidas tales como COUNT(), SUM(), MIN(), MAX(), etc.
    • Nuevas funciones también (p ROW_NUMBER(). Ej . RATION_TO_REOIRT(), Etc.)


Más información con ejemplo: http://msdn.microsoft.com/en-us/library/ms189461.aspx

Elshan
fuente
-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

Ese es el resultado de la consulta. La tabla utilizada como fuente es el mismo exept que no tiene última columna. Esta columna es una suma móvil de la tercera.

Consulta:

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

(la tabla va como public.iuk)

sql version:  2012

Está un poco por encima del nivel de dbase (1986), no sé por qué se han necesitado más de 25 años para terminarlo.

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