Voltar para a lista de artigos Artigos
15 minutos de leitura

Como usar SUM() com OVER(PARTITION BY) no SQL

Descubra casos de uso reais da função SUM() com a cláusula OVER(PARTITION BY). Aprenda a sintaxe e confira 5 exemplos diferentes.

Usamos as funções de janela do SQL para realizar operações em grupos de dados. Essas operações incluem as funções matemáticas SUM(), COUNT(), AVG(), entre outras. Neste artigo, explicaremos o que SUM() com OVER(PARTITION BY) faz no SQL. Mostraremos os casos de uso mais comuns em aplicativos do mundo real para determinar a proporção entre o valor da linha individual e o valor total, calcular totais em execução e encontrar um total de pedido personalizado que inclua descontos para determinados produtos.

Para obter um guia detalhado sobre o uso do site SUM() com OVER() e outras funções de janela, assista ao nosso Funções de Janela (Window Functions) em SQL curso interativo. Ele explica detalhadamente todos os conceitos de função de janela SQL e tem mais de 200 exercícios.

Vamos começar.

A cláusula OVER() no SQL

No SQL, a cláusula OVER() é usada para introduzir funções de janela. A sintaxe geral é:

SELECT …
  <window function> OVER(...)
…

OVER() informa ao banco de dados que queremos usar funções de janela. A função de janela pode ser uma função agregada, como SUM(), ou outra função de janela.

Uma função de janela funciona em um "quadro de janela" ou em um conjunto de linhas relacionadas à linha atual. OVER() define o quadro de janela para cada linha. Uma cláusula OVER() vazia informa ao banco de dados que todo o conjunto de resultados é a moldura da janela.

Vamos dar uma olhada em um exemplo da sintaxe SUM() OVER(). Usaremos a tabela employees da tabela de dados de amostra fornecida pelo Oracle Live SQL. Essa tabela consiste nas seguintes colunas:

  • emp_id é o identificador do funcionário.
  • name é o nome do funcionário.
  • job é o título do cargo.
  • dept_id é o identificador do departamento.
  • salary é o salário do funcionário.
EMP_IDNAMEJOBDEPT_IDSALARY
7839KINGPRESIDENT105000
7698BLAKEMANAGER302850
7900JAMESCLERK30950
7934MILLERCLERK101300

Podemos usar a função SUM() com a cláusula OVER() para obter o salário total de todos os funcionários e exibir o salário total ao lado do salário de cada funcionário.

SELECT 
  emp_id, 
  name, 
  job, 
  dept_id, 
  salary, 
  SUM(salary) OVER() AS total_salary
FROM employees;
emp_idNAMEJOBDEPT_IDSALARYTOTAL_SALARY
7839KINGPRESIDENT10500029025
7698BLAKEMANAGER30285029025
7900JAMESCLERK3095029025
7934MILLERCLERK10130029025

A função SUM() é executada para cada linha da tabela. Toda vez que é chamada, ela obtém as informações de salário de todas as linhas da tabela para calcular o valor total do salário. O salário total é exibido ao lado dos detalhes de cada linha individual. Os detalhes das linhas individuais são preservados e exibidos ao lado do salário total.

Neste exemplo, a moldura da janela (o conjunto de linhas em que o SUM() está operando) é o conjunto de dados inteiro. Você pode adicionar cláusulas adicionais em OVER() para alterar o quadro da janela.

Exemplo 1A: Cálculo da soma de cada grupo - SUM() com OVER(PARTITION BY ...)

A cláusula OVER() pode conter detalhes sobre a forma como queremos particionar os dados. Usamos a cláusula PARTITION BY em OVER() para dividir os dados em partições ou grupos. O uso de PARTITION BY é semelhante ao uso de GROUP BY, pois as linhas são particionadas em grupos com base no valor de algumas colunas. Quando usamos o SUM() OVER(PARTITION BY …), podemos calcular a soma dos valores de cada grupo ou partição de dados. Por exemplo, podemos calcular o salário total de cada departamento:

SELECT 
  emp_id, 
  name, 
  job, 
  dept_id, 
  salary,
  SUM(salary) OVER(PARTITION BY dept_id) AS dept_total_salary
FROM employees;

Aqui está o resultado:

EMP_IDNAMEJOBDEPT_IDSALARYDEPT_TOTAL_SALARY
7782CLARKMANAGER1024508750
7934MILLERCLERK1013008750
7839KINGPRESIDENT1050008750
7902FORDANALYST20300010875
7788SCOTTANALYST20300010875
7566JONESMANAGER20297510875
7369SMITHCLERK2080010875
7876ADAMSCLERK20110010875
7521WARDSALESMAN3012509400
7654MARTINSALESMAN3012509400
7844TURNERSALESMAN3015009400
7900JAMESCLERK309509400
7499ALLENSALESMAN3016009400
7698BLAKEMANAGER3028509400

Usamos SUM(salary) OVER(PARTITION BY dept_id) para obter o salário total por departamento. A cláusula PARTITION BY divide as linhas em grupos com base na coluna dept_id. As linhas com dept_id igual a 10 são colocadas em um grupo (marcado em amarelo na tabela acima), as linhas com dept_id igual a 20 são colocadas em outro grupo (marcado em verde) e, finalmente, as linhas com dept_id igual a 30 são colocadas em outro grupo (marcado em vermelho). A função SUM() calcula a soma das linhas em cada grupo.

Observe que, ao usar SUM() OVER(PARTITION BY), você mantém os detalhes das linhas individuais. Você pode, por exemplo, ver os detalhes do funcionário chamado Ford: seu cargo, seu salário e como ele se compara ao total de salários em seu departamento.

Esse é o uso mais comum do site SUM() OVER(PARTITION BY): você calcula o valor da soma para cada grupo de dados e mantém os detalhes das linhas individuais. Vejamos um exemplo semelhante.

Exemplo 1B: Cálculo do salário para cada cargo

Podemos usar SUM(salary) OVER(PARTITION BY job) para obter o salário total por cargo. Vamos dar uma olhada:

SELECT 
  emp_id, 
  name, 
  job, 
  dept_id, 
  salary,
  SUM(salary) OVER(PARTITION BY job) AS job_total_salary
FROM employees;

Aqui está o resultado:

EMP_IDNAMEJOBDEPT_IDSALARYJOB_TOTAL_SALARY
7782CLARKMANAGER1024508275
7698BLAKEMANAGER3028508275
7566JONESMANAGER2029758275
7934MILLERCLERK1013004150
7369SMITHCLERK208004150
7876ADAMSCLERK2011004150
7900JAMESCLERK309504150
7902FORDANALYST2030006000
7788SCOTTANALYST2030006000
7521WARDSALESMAN3012505600
7654MARTINSALESMAN3012505600
7844TURNERSALESMAN3015005600
7499ALLENSALESMAN3016005600
7839KINGPRESIDENT1050005000

Desta vez, as linhas são agrupadas com base no valor do cargo e não no ID do departamento. Os funcionários com o mesmo cargo são colocados em um grupo e calculamos o salário total das pessoas com esse cargo. A função SUM() é aplicada a todos os salários em cada grupo: o salário total do grupo "Manager" é a soma de 2450, 2850 e 2975, que são os salários dos três gerentes que estão em nossa tabela.

Exemplo 2: Calcular a proporção de um valor de uma única linha em relação ao total

Normalmente, queremos ver a comparação entre cada linha individual e a soma total. Vamos calcular a porcentagem de cada salário individual em relação ao total de salários em seu departamento.

SELECT 
  emp_id, 
  name, 
  job, 
  dept_id, 
  salary,
  ROUND(100.0 * salary / SUM(salary) OVER(PARTITION BY dept_id), 2) 
           AS percentage_of_dept_total_salary
FROM employees;
EMP_IDNAMEJOBDEPT_IDSALARYPERCENTAGE_OF_DEPT_TOTAL_SALARY
7782CLARKMANAGER10245028
7934MILLERCLERK10130014.86
7839KINGPRESIDENT10500057.14
7902FORDANALYST20300027.59
7788SCOTTANALYST20300027.59
7566JONESMANAGER20297527.36
7369SMITHCLERK208007.36
7876ADAMSCLERK20110010.11
7521WARDSALESMAN30125013.3
7654MARTINSALESMAN30125013.3
7844TURNERSALESMAN30150015.96
7900JAMESCLERK3095010.11
7499ALLENSALESMAN30160017.02
7698BLAKEMANAGER30285030.32

Desta vez, as linhas são agrupadas com base no valor job em vez do ID do departamento. Os funcionários com o mesmo cargo são colocados em um grupo e calculamos o salário total das pessoas nesse cargo. A função SUM() é aplicada a todos os salários em cada grupo: o salário total do grupo "Manager" é a soma de 2450, 2850 e 2975, que são os salários dos três gerentes que estão em nossa tabela.

Exemplo 2: Calcular a proporção de um valor de uma única linha em relação ao total

Normalmente, queremos ver a comparação entre cada linha individual e a soma total. Vamos calcular a porcentagem de cada salário individual em relação ao total de salários em seu departamento.

SELECT 
  emp_id, 
  name, 
  job, 
  dept_id, 
  salary,
  ROUND(100.0 * salary / SUM(salary) OVER(PARTITION BY dept_id), 2) 
           AS percentage_of_dept_total_salary
FROM employees;
EMP_IDNAMEJOBDEPT_IDSALARYPERCENTAGE_OF_DEPT_TOTAL_SALARY
7782CLARKMANAGER10245028
7934MILLERCLERK10130014.86
7839KINGPRESIDENT10500057.14
7902FORDANALYST20300027.59
7788SCOTTANALYST20300027.59
7566JONESMANAGER20297527.36
7369SMITHCLERK208007.36
7876ADAMSCLERK20110010.11
7521WARDSALESMAN30125013.3
7654MARTINSALESMAN30125013.3
7844TURNERSALESMAN30150015.96
7900JAMESCLERK3095010.11
7499ALLENSALESMAN30160017.02
7698BLAKEMANAGER30285030.32

Podemos ver, por exemplo, que o analista Scott tem um salário individual de 3.000; isso representa 27,59% do total de salários em seu departamento.

Aqui está um detalhamento das funções usadas para realizar essa tarefa:

  • Pegamos cada salário individual e o dividimos pelo salário total do departamento:

salary / SUM(salary) OVER(PARTITION BY dept_id)

  • Para obter uma porcentagem, multiplicamos por 100%:
    • 0 * salary / SUM(salary) OVER(PARTITION BY dept_id)
  • Em seguida, usamos a função ROUND() para obter dois dígitos decimais:

ROUND(100.0 * salary / SUM(salary) OVER(PARTITION BY dept_id), 2)

O cálculo da proporção entre a linha individual e a soma de um grupo é outro caso de uso muito comum de SUM() com OVER(PARTITION BY). De maneira semelhante, você poderia calcular a diferença entre o total do grupo e a linha individual.

Qual é a diferença entre SUM() com OVER(PARTITION BY …) e usar SUM() com GROUP BY?

A cláusula PARTITION BY realiza uma tarefa semelhante à da cláusula GROUP BY. Tanto OVER(PARTITION BY) quanto GROUP BY dividem o conjunto de dados em partições ou grupos. Ao usar a função SUM(), ambas as expressões calculam a soma de cada grupo. Então, qual delas você deve escolher?

Ao usar GROUP BY, os detalhes de linhas individuais são recolhidos. Dê uma olhada:

SELECT 
  job, 
  SUM(salary) AS total_salary
FROM employees
GROUP BY job;
JOBTOTAL_SALARY
ANALYST6000
CLERK4150
SALESMAN5600
MANAGER8275
PRESIDENT5000

Aqui, obtemos o valor total do salário para cada cargo sem considerar os salários individuais dos funcionários.

No entanto, se você quiser comparar o valor da soma total com os valores das linhas individuais (por exemplo, para calcular a proporção entre o valor individual e o total), o caminho a seguir é OVER(PARTITION BY …). Ele retorna todas as linhas individuais juntamente com o valor da soma total de cada linha. Esse valor de soma total pode ser diferente, dependendo da partição à qual a linha pertence.

SELECT 
  name, 
  job, 
  salary, 
  SUM(salary) OVER(PARTITION BY job) AS total_salary
FROM employees;
NAMEJOBSALARYTOTAL_SALARY
FORDANALYST30006000
SCOTTANALYST30006000
SMITHCLERK8004150
JAMESCLERK9504150
ADAMSCLERK11004150
MILLERCLERK13004150
BLAKEMANAGER28508275
JONESMANAGER29758275
CLARKMANAGER24508275
KINGPRESIDENT50005000
TURNERSALESMAN15005600
ALLENSALESMAN16005600
WARDSALESMAN12505600
MARTINSALESMAN12505600

O valor total_salary corresponde aos resultados da consulta anterior com GROUP BY. Mas aqui, você também pode ver os salários individuais. Por exemplo, há dois analistas que ganham 6.000 no total; cada um deles ganha 3.000.

A regra geral é: se você quiser saber apenas o valor da soma de cada grupo e não estiver interessado nos detalhes de cada linha individual, deverá usar a cláusula GROUP BY. Se você estiver interessado tanto na soma de cada grupo quanto nos detalhes de cada linha individual, use SUM() OVER(PARTITION BY).

Exemplo 3: cálculo de um total em execução usando SUM() com OVER(PARTITION BY)

Outro uso comum da sintaxe SUM() OVER(...) é calcular o total em execução.

Um total em execução é a soma cumulativa dos números anteriores em uma coluna. Usamos totais em execução para calcular valores que se acumulam ao longo do tempo. Por exemplo, com um total em execução, você pode calcular o uso mensal de dados móveis adicionando o valor de cada dia subsequente à soma dos valores dos dias anteriores. Da mesma forma, é possível calcular como o número de usuários registrados aumenta a cada dia ou como a receita total aumenta a cada transação.

Usamos a sintaxe SUM() com OVER(PARTITION BY … ORDER BY …) para calcular o total em execução.

Vamos dar uma olhada em um exemplo. Usaremos a tabela orders de dados de amostra fornecidos pelo Oracle Live SQL. Essa tabela armazena dados históricos de pedidos. Cada pedido tem sua data (order_date), representante de vendas (sales_rep_id) e valor total (order_total).

Aqui, calculamos o valor total em execução para cada representante de vendas:

SELECT 
  order_date, 
  sales_rep_id, 
  order_total,
  SUM(order_total) OVER(PARTITION BY sales_rep_id ORDER BY order_date) 
          AS running_total
FROM orders;
ORDER_DATESALES_REP_IDORDER_TOTALRUNNING_TOTAL
29-MAR-07 02.22.40.536996 PM15310794.610794.6
16-AUG-07 03.34.12.234359 PM15378279.689074.2
04-OCT-07 09.53.34.362632 PM15312989203.2
21-NOV-07 10.22.33.263332 AM15313824103027.2
16-DEC-07 08.19.55.462332 PM15311188.5114215.7
27-JUL-06 12.22.59.662632 PM15452471.952471.9
27-JUL-06 01.34.16.562632 PM154364656117.9
29-JUN-07 09.53.41.984501 AM1544856165.9
01-JUL-07 04.49.13.615512 PM15422056385.9
02-JUL-07 03.34.44.665170 AM15460056985.9
01-SEP-07 09.53.26.934626 AM154545162436.9
02-OCT-07 05.49.34.678340 PM1546653.469090.3
10-NOV-07 03.49.25.526321 AM15450125119215.3
19-NOV-07 02.41.54.696211 PM15442283.2161498.5
17-DEC-07 05.03.52.562632 PM15410474.6171973.1

Para isso, particionamos nossa tabela por representante de vendas e, em seguida, ordenamos cada partição por data:

	SUM(order_total) OVER(PARTITION BY sales_rep_id ORDER BY order_date)

O novo elemento de sintaxe aqui é ORDER BY. Ele define como as linhas são classificadas em cada quadro de janela. A função SUM() é aplicada a cada linha; ela soma o valor atual order_total ao valor anterior running_total; os totais em execução são calculados separadamente para cada partição (aqui, para cada ID de representante de vendas).

Vamos dar uma olhada mais de perto no representante de vendas com ID 153. Seu primeiro pedido foi feito em29 de março e tinha um valor total de 1.794,6. Nesse momento, o total em execução (a soma) é igual ao valor do pedido. Seu segundo pedido foi em16 de agosto, no valor de 78279,6; agora o total em execução é igual à soma dos valores do primeiro e do segundo pedidos (10794,6 + 78279,6 = 89074,2). Após o terceiro pedido, o total em execução é igual ao total em execução anterior mais o valor do terceiro pedido (89074,2 + 129 = 89203,2). Esse processo é semelhante para o representante de vendas com o ID 154.

O cálculo de um total em execução é um padrão comum ao usar o SQL para análise de dados. Você pode ler como calcular um total em execução no SQL em outra parte do nosso blog.

Exemplo 4: Contagem de objetos em categorias personalizadas

A função SUM() é frequentemente combinada com a instrução CASE WHEN para contar objetos em categorias personalizadas. Por exemplo, talvez você queira computar o salário total dos funcionários em cargos de gerência em um departamento e exibi-lo ao lado dos detalhes de cada funcionário. Você pode fazer isso usando SUM() OVER(PARTITION BY) em combinação com CASE WHEN.

Vamos primeiro analisar a instrução CASE WHEN. Ela é semelhante à instrução if, comum em muitas linguagens de programação. Nós a usamos para definir o valor de uma expressão em diferentes situações ou casos.

No exemplo a seguir, usamos a instrução CASE WHEN para identificar cada funcionário como gerência (gerentes e presidentes) ou como funcionário comum (todos os outros cargos). Dê uma olhada:

SELECT 
  emp_id, 
  name, 
  job, 
  dept_id, 
  CASE WHEN job IN (‘PRESIDENT’, ‘MANAGER’) 
    THEN ‘MANAGEMENT’ 
    ELSE ‘REGULAR’ END AS emp_type
  salary
FROM employees;

O comando CASE avalia todas as condições do WHEN. Se encontrar a condição correspondente, retorna o valor no ramo THEN. Se não encontrar a condição correspondente, ele retornará o valor fornecido após ELSE. Em nosso exemplo, os funcionários no cargo de presidente ou gerente são rotulados como 'Management'. Todos os outros cargos recebem o rótulo "Regular". Aqui está o resultado da consulta:

EMP_IDNAMEJOBDEPT_IDEMP_TYPESALARY
7782CLARKMANAGER10MANAGEMENT2450
7934MILLERCLERK10REGULAR1300
7839KINGPRESIDENT10MANAGEMENT5000
7902FORDANALYST20REGULAR3000
7788SCOTTANALYST20REGULAR3000
7566JONESMANAGER20MANAGEMENT2975
7369SMITHCLERK20REGULAR800
7876ADAMSCLERK20REGULAR1100
7521WARDSALESMAN30REGULAR1250
7654MARTINSALESMAN30REGULAR1250
7844TURNERSALESMAN30REGULAR1500
7900JAMESCLERK30REGULAR950
7499ALLENSALESMAN30REGULAR1600
7698BLAKEMANAGER30MANAGEMENT2850

Você pode ler mais sobre CASE em nosso artigo Como utilizar o CASO em SQL.

Você também pode usar a instrução CASE com SUM() para somar valores em categorias personalizadas. Aqui está a consulta:

SELECT
  dept_id,
  SUM(
    CASE WHEN job IN (‘PRESIDENT’, ‘MANAGER’) 
    THEN salary
    ELSE 0 END) 
    AS dept_management_salary,
  SUM(
    CASE WHEN job IN (‘PRESIDENT’, ‘MANAGER’) 
    THEN 0
    ELSE salary END) 
    AS dept_regular_salary
FROM employees
GROUP BY dept_id;
DEPT_IDDEPT_MANAGEMENT_SALARYDEPT_REGULAR_SALARY
1074501300
2029757900
3028506550

Para funcionários em cargos de gerência, a primeira instrução CASE retorna o valor da coluna salary. A função SUM(), combinada com GROUP BY, soma todos os salários dos funcionários de gerência com o mesmo dept_id. Dessa forma, calculamos o salário total de gerência por departamento. A segunda instrução CASE calcula o salário total do departamento para todos os funcionários regulares.

Você pode calcular o salário total dos funcionários em posição de gerência em um departamento e exibi-lo ao lado dos detalhes do funcionário usando SUM() com PARTITION BY:

SELECT 
  emp_id, 
  name, 
  job, 
  dept_id, 
  CASE WHEN job IN (‘PRESIDENT’, ‘MANAGER’) 
    THEN ‘MANAGEMENT’ 
    ELSE ‘REGULAR’ END AS emp_type
  salary,
  SUM(
    CASE WHEN job IN (‘PRESIDENT’, ‘MANAGER’) 
    THEN salary 
    ELSE 0 END) OVER(PARTITION BY dept_id) AS dept_management_salary
FROM employees;
EMP_IDNAMEJOBDEPT_IDEMP_TYPESALARYDEPT_MANAGEMENT_SALARY
7782CLARKMANAGER10MANAGEMENT24507450
7934MILLERCLERK10REGULAR13007450
7839KINGPRESIDENT10MANAGEMENT50007450
7902FORDANALYST20REGULAR30002975
7788SCOTTANALYST20REGULAR30002975
7566JONESMANAGER20MANAGEMENT29752975
7369SMITHCLERK20REGULAR8002975
7876ADAMSCLERK20REGULAR11002975
7521WARDSALESMAN30REGULAR12502850
7654MARTINSALESMAN30REGULAR12502850
7844TURNERSALESMAN30REGULAR15002850
7900JAMESCLERK30REGULAR9502850
7499ALLENSALESMAN30REGULAR16002850
7698BLAKEMANAGER30MANAGEMENT28502850

As linhas são divididas em grupos com base na coluna dept_id. Há três grupos, um para cada departamento. A função SUM() é aplicada à expressão CASE WHEN. Em vez de calcular o salário total em cada departamento, calculamos o salário total dos funcionários em cargos de gerência no departamento. O CASE retorna 0 para os funcionários regulares (a soma não é aumentada) e o valor do salário para os funcionários gerenciais. Você pode usar esse padrão em muitas situações diferentes. De fato, na próxima seção, veremos outro exemplo de combinação de CASE WHEN com SUM() e OVER(PARTITION BY).

Exemplo 5: Cálculo de um total de pedido com descontos usando SUM() com CASE WHEN

Neste exemplo, queremos calcular o valor total de cada pedido, incluindo descontos para alguns itens do pedido. Usaremos a sintaxe SUM() OVER(PARTITION BY …) para somar todos os valores de itens por pedido. E para incorporar os descontos do produto, usaremos a instrução CASE WHEN.

Usamos a tabela order_items de dados de amostra fornecidos pelo Oracle Live SQL. Essa tabela armazena todos os itens do pedido (product_id) pertencentes a cada pedido (order_id). Ela contém informações sobre o preço unitário do produto (unit_price) e a quantidade do pedido (quantity).

Agora, queremos calcular os valores do pedido: um para cada produto do pedido e um total para o pedido; o total do pedido deve incluir quaisquer descontos aplicados aos produtos.

O total do pedido é igual ao preço unitário multiplicado pela quantidade do pedido. Entretanto, quando houver um desconto aplicado a um produto, usaremos um demonstrativo CASE WHEN para contabilizá-lo.

SELECT 
  order_id, 
  product_id, 
  unit_price, 
  quantity,   

  SUM(CASE 
     -- 20% discount for this product
     WHEN product_id = 3143 THEN unit_price*quantity*0.8
     -- 40% discount for this product
     WHEN product_id = 3106 THEN unit_price*quantity*0.6
     ELSE unit_price*quantity END)
  OVER(PARTITION BY order_id, product_id) 
        AS order_product_total_with_discount,

  SUM(CASE 
     -- 20% discount for this product
     WHEN product_id = 3143 THEN unit_price*quantity*0.8
     -- 40% discount for this product
     WHEN product_id = 3106 THEN unit_price*quantity*0.6
     ELSE unit_price*quantity END)
  OVER(PARTITION BY order_id) 
        AS order_total_with_discount    

FROM order_items;
ORDER_IDPRODUCT_IDUNIT_PRICEQUANTITYORDER_PRODUCT_TOTAL_WITH_DISCOUNTORDER_TOTAL_WITH_DISCOUNT
2354310648611756.844916.2
2354311496.8434162.444916.2
235431237947371344916.2
235431294147192744916.2
235431392148100844916.2
235431431653678.444916.2
23543150175898644916.2
235431633061183044916.2
235431653764236844916.2
235431675168346844916.2
23543170145.2701016444916.2
23543176113.3728157.644916.2
235431826177469744916.2
2355228946200920094513.5
23552308571851054594513.5
2355231186.918816337.294513.5
2355232219188357294513.5
2355232317190323094513.5
235523261.1192211.294513.5
235523301.1197216.794513.5
2355233925199497594513.5
23552359226.620446226.494513.5

Para calcular o total por produto em um pedido, usamos a seguinte sintaxe:

SUM(CASE 
       WHEN product_id=3143 THEN unit_price*quantity*0.8 -- 20% discount 
       WHEN product_id=3106 THEN unit_price*quantity*0.6 -- 40% discount 
       ELSE unit_price*quantity END)
OVER(PARTITION BY order_id, product_id) AS order_product_total_with_discount

Passamos a instrução CASE WHEN como um argumento para a função SUM(); se o ID do produto da linha atual for 3143, aplicaremos um desconto de 20%; para o ID do produto 3106, o desconto será de 40%. Em seguida, particionamos o conjunto de dados por ID do pedido e ID do produto para obter os valores totais de cada produto em um pedido.

Observe que usamos duas cláusulas PARTITION BY diferentes na consulta. Para calcular o valor total do produto, particionamos o conjunto de dados por ID do pedido e ID do produto. Para calcular o total por pedido, particionamos o conjunto de dados somente pelo ID do pedido. O valor total do pedido é igual para todas as linhas com o mesmo ID de pedido. Ou seja, se você somar todos os valores totais do produto de um determinado pedido, obterá o valor total do pedido. Por exemplo, para o ID do pedido de 2355, obtemos o seguinte: 9200 + 10545 + 16337.2 + 3572 + 3230 + 211.2 + 216.7 + 4975 + 46226.4 = 94513.5.

Aqui estão alguns exemplos da função SUM() com CASE WHEN.

Vá em frente e use SUM() com OVER() e PARTITION BY

Agora que você já sabe como usar a função de janela SUM() e suas várias opções de sintaxe, pode experimentá-la você mesmo.

Apresentamos exemplos de como calcular uma porcentagem de um valor de uma única linha em relação ao total da partição e mostramos como calcular o total em execução e o total personalizado da ordem. Recomendamos que você pratique com seus próprios dados.

Para saber mais, experimente nosso curso interativo Funções de Janela (Window Functions) em SQL curso interativo, que explica detalhadamente todos os conceitos das funções de janela. Ou confira nossa Folha de dicas Funções de Janela (Window Functions) em SQL se você quiser uma referência rápida e fácil sobre as funções de janela do SQL.

Boa sorte!