Bora imaginar: você tá acompanhando as receitas da sua empresa, vendas da sua loja online ou até só analisando seus gastos do ano. Você não quer só ver quanto ganhou ou gastou em cada mês, mas também entender como isso vai acumulando de mês em mês.
Funções agregadoras normais (GROUP BY) não vão ajudar aqui — elas agrupam os dados e devolvem uma linha por grupo. Mas e se a gente quiser ver cada mês e ao mesmo tempo calcular a soma acumulada? É aí que SUM() junto com funções de janela salva a gente.
O básico de funções de janela pra somas acumuladas
Funções de janela deixam você fazer operações agregadas em janelas. Assim, dá pra somar valores em cada linha, mas sem sumir com as outras linhas. Chega de sacrificar dados por causa do GROUP BY!
Sintaxe do SUM() com função de janela
Aqui vai um template básico pra usar soma acumulada:
SELECT
column_name,
SUM(column_name) OVER (PARTITION BY partition_column ORDER BY order_column) AS cumulative_sum
FROM
table_name;
O que rola aqui:
SUM(column_name)— soma os valores.OVER()— define a janela pra calcular.PARTITION BY— separa os dados em grupos (opcional).ORDER BY— define a ordem das linhas dentro da janela.
Exemplo: receita acumulada por mês
Imagina uma tabela das suas receitas:
| mês | receita |
|---|---|
| 2023-01 | 1000 |
| 2023-02 | 1500 |
| 2023-03 | 2000 |
A gente quer ver a receita de cada mês e o acumulado. Bora montar a query SQL:
SELECT
mês,
receita,
SUM(receita) OVER (ORDER BY mês) AS receita_acumulada
FROM
receitas;
Resultado:
| mês | receita | receita_acumulada |
|---|---|---|
| 2023-01 | 1000 | 1000 |
| 2023-02 | 1500 | 2500 |
| 2023-03 | 2000 | 4500 |
O que tá rolando:
ORDER BY mêsdentro doOVER()diz pro PostgreSQL considerar as linhas em ordem cronológica.- Pra cada linha, a soma é feita levando em conta todas as linhas anteriores (e a atual).
Pensa bem no que acontece aqui. Na primeira linha, SUM() soma só a primeira linha, na segunda — soma as duas primeiras, na terceira — soma as três. Por isso a ordem dos meses é super importante!
Exemplo: receita acumulada por região
Se você tivesse uma tabela de vendas por região, um pedaço dela seria assim:
| região | mês | receita |
|---|---|---|
| Norte | 2023-01 | 1000 |
| Norte | 2023-02 | 1500 |
| Sul | 2023-01 | 2000 |
| Sul | 2023-02 | 2500 |
Agora a gente quer calcular a receita acumulada separada pra cada região:
SELECT
região,
mês,
receita,
SUM(receita) OVER (PARTITION BY região ORDER BY mês) AS receita_acumulada
FROM
vendas;
O resultado vai ser assim:
| região | mês | receita | receita_acumulada |
|---|---|---|---|
| Norte | 2023-01 | 1000 | 1000 |
| Norte | 2023-02 | 1500 | 2500 |
| Sul | 2023-01 | 2000 | 2000 |
| Sul | 2023-02 | 2500 | 4500 |
Agora cada região é analisada separadamente (PARTITION BY região), mas dentro da região as linhas ficam em ordem de tempo (ORDER BY mês).
Média móvel (AVG())
Beleza, somas acumuladas são massa, mas e se você quiser analisar tendências, tipo, dos últimos 3 meses? Pra isso serve a média móvel.
Exemplo: média móvel das receitas
De novo, vamos usar a tabela receitas, e os dados são:
| mês | receita |
|---|---|
| 2023-01 | 1000 |
| 2023-02 | 1500 |
| 2023-03 | 2000 |
| 2023-04 | 2500 |
Query pra calcular a média móvel de 3 meses:
SELECT
mês,
receita,
AVG(receita) OVER (
ORDER BY mês
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS media_movel
FROM
receitas;
Resultado:
| mês | receita | media_movel |
|---|---|---|
| 2023-01 | 1000 | 1000 |
| 2023-02 | 1500 | 1250 |
| 2023-03 | 2000 | 1500 |
| 2023-04 | 2500 | 2000 |
Explicando:
ROWS BETWEEN 2 PRECEDING AND CURRENT ROWdiz pro PostgreSQL olhar pra linha atual e as duas anteriores pra calcular a média.- No fim, pra cada mês a gente vê a média das receitas dos últimos 3 meses.
Ou seja, pra cada linha a gente define uma janela de 3 linhas: a atual e as duas anteriores. Depois calcula a média. Muito prático.
Como o ORDER BY afeta tudo
Funções de janela dependem da ordem certa das linhas. Se a ordem estiver errada (ou nem for definida), os resultados podem ficar bem estranhos.
Exemplo: erro por falta de ORDER BY
Se a gente tirar o ORDER BY do OVER(), em vez da soma acumulada, vamos pegar a soma total das receitas em cada linha:
SELECT
mês,
receita,
SUM(receita) OVER () AS soma_acumulada_errada
FROM
receitas;
Resultado:
| mês | receita | soma_acumulada_errada |
|---|---|---|
| 2023-01 | 1000 | 7000 |
| 2023-02 | 1500 | 7000 |
| 2023-03 | 2000 | 7000 |
| 2023-04 | 2500 | 7000 |
As linhas não estão ordenadas, e em vez da soma acumulada, a função só soma tudo pra cada linha, sem critério.
Casos reais de uso
Análise de receitas:
- Somas acumuladas ajudam a acompanhar como as vendas ou receitas da empresa crescem.
- Média móvel mostra a tendência "limpa", sem ruídos.
Modelagem financeira:
Bancos e empresas financeiras usam funções de janela pra analisar pagamentos, crescimento de dívidas e outras métricas.
Análise de séries temporais:
Dados de tempo, tipo número de usuários online, visualizações de página, faturamento etc., são perfeitos pra analisar com SUM() e AVG().
GO TO FULL VERSION