A cláusula SQL OVER () - quando e por que é ú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);

Eu li sobre essa cláusula e não entendo por que preciso dela. O que a função Overfaz? O que Partitioning Byfaz? Por que não consigo fazer uma consulta com a escrita Group By SalesOrderID?

Com cores voadoras
fonte
30
Não importa qual RDBMS você usa, o tutorial do Postgres pode ser útil. Tem exemplos; me ajudou.
Andrew Lazarus

Respostas:

144

Você pode usar GROUP BY SalesOrderID. A diferença é que, com GROUP BY, você pode ter apenas os valores agregados para as colunas que não estão incluídas em GROUP BY.

Por outro lado, usando funções agregadas em janelas em vez de GROUP BY, você pode recuperar valores agregados e não agregados. Ou seja, embora você não esteja fazendo isso na sua consulta de exemplo, você pode recuperar os OrderQtyvalores individuais e suas somas, contagens, médias etc. em grupos dos mesmos SalesOrderIDs.

Aqui está um exemplo prático de por que os agregados com janelas são ótimos. Suponha que você precise calcular qual a porcentagem de um total de cada valor. Sem agregados em janela, você teria que derivar primeiro uma lista de valores agregados e depois associá-la ao conjunto de linhas original, ou seja:

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]

Agora veja como você pode fazer o mesmo com um agregado em janela:

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

Muito mais fácil e limpo, não é?

Andriy M
fonte
68

A OVERcláusula é poderosa, pois você pode ter agregados em diferentes intervalos ("janelas"), independentemente de você usar GROUP BYou não

Exemplo: obter contagem por SalesOrderIDe contagem 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

Obter COUNTs diferentes , nãoGROUP 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
fonte
47

Se você quisesse apenas Agrupar por SalesOrderID, não seria possível incluir as colunas ProductID e OrderQty na cláusula SELECT.

A cláusula PARTITION BY permite que você divida suas funções agregadas. Um exemplo óbvio e útil seria se você desejasse gerar números de linha para linhas de pedido em um 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

(Minha sintaxe pode estar um pouco desligada)

Você retornaria 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
fonte
42

Deixe-me explicar com um exemplo e você poderá ver como isso funciona.

Supondo que você tenha a seguinte tabela DIM_EQUIPMENT:

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

Executar abaixo do SQL

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

O resultado seria o seguinte

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

Veja o que aconteceu.

Você pode contar sem Agrupar por no ANO e Combinar com ROW.

Outra maneira interessante de obter o mesmo resultado, se abaixo, usando a cláusula WITH, WITH funciona como VIEW in-line e pode simplificar a consulta, especialmente as mais complexas, o que não é o caso aqui, já que estou apenas tentando mostrar o 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
fonte
17

A cláusula OVER, quando combinada com PARTITION BY, afirma que a chamada de função anterior deve ser feita analiticamente, avaliando as linhas retornadas da consulta. Pense nisso como uma instrução GROUP BY embutida.

OVER (PARTITION BY SalesOrderID) está informando que, para a função SUM, AVG, etc ..., retorne o valor OVER um subconjunto dos registros retornados da consulta e PARTITION que subconjunto BY a chave estrangeira SalesOrderID.

Então, somaremos todos os registros OrderQty para CADA ORÇAMENTO DE VENDA ÚNICO, e esse nome de coluna será chamado 'Total'.

É um meio MUITO mais eficiente do que usar várias visualizações embutidas para descobrir as mesmas informações. Você pode colocar essa consulta em uma exibição embutida e filtrar no Total então.

SELECT ...,
FROM (your query) inlineview
WHERE Total < 200
maple_shaft
fonte
2
  • Query PetitionCláusula também chamada .
  • Semelhante à Group ByCláusula

    • dividir dados em partes (ou partições)
    • separar por limites de partição
    • função executa dentro de partições
    • reinicializado ao cruzar o limite de partição

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

  • Funções

    • Funções conhecidas, tais como COUNT(), SUM(), MIN(), MAX(), etc.
    • Novas funções, bem como (por exemplo ROW_NUMBER(), RATION_TO_REOIRT(), etc.)


Mais informações com o exemplo: http://msdn.microsoft.com/en-us/library/ms189461.aspx

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

Isso é resultado da consulta. A tabela usada como fonte é o mesmo exceção que não possui a última coluna. Esta coluna é uma soma móvel da terceira.

Inquerir:

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

(a tabela vai como public.iuk)

sql version:  2012

É um pouco acima do nível dbase (1986), não sei por que mais de 25 anos foram necessários para finalizar.

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