Chegou a hora de falar das tretas que podem rolar quando você mexe com funções window. Como sempre, em programação (e na vida), é melhor aprender com os erros dos outros. Bora analisar os vacilos mais comuns que a galera comete — tanto quem tá começando quanto dev experiente — e aprender a não cair nessas armadilhas.
Erro #1: Uso errado do PARTITION BY
Um dos erros mais frequentes é esquecer ou colocar errado o parâmetro PARTITION BY, principalmente quando você quer separar os dados em grupos. Sem ele, o PostgreSQL vai considerar todas as linhas como um grupão só, e aí o resultado pode ser bem diferente do que você espera.
Imagina que a gente tem uma tabela sales com dados de vendas:
| id | region | month | total |
|---|---|---|---|
| 1 | North | 2023-01 | 1000 |
| 2 | South | 2023-01 | 800 |
| 3 | North | 2023-02 | 1200 |
| 4 | South | 2023-02 | 900 |
Você quer calcular a soma acumulada (SUM()) das vendas por mês pra cada região. Dá pra escrever uma query assim:
SELECT
region,
month,
SUM(total) OVER (ORDER BY month) AS running_total
FROM
sales;
Resultado:
| region | month | running_total |
|---|---|---|
| North | 2023-01 | 1000 |
| South | 2023-01 | 1800 |
| North | 2023-02 | 3000 |
| South | 2023-02 | 3900 |
De primeira parece que tá tudo certo. Mas o resultado não bate com o esperado, porque a soma acumulada tá sendo feita pra todas as linhas, não por região. O problema é que esquecemos de colocar o PARTITION BY region.
Código corrigido:
SELECT
region,
month,
SUM(total) OVER (PARTITION BY region ORDER BY month) AS running_total
FROM
sales;
Resultado:
| region | month | running_total |
|---|---|---|
| North | 2023-01 | 1000 |
| North | 2023-02 | 2200 |
| South | 2023-01 | 800 |
| South | 2023-02 | 1700 |
Agora sim: os dados estão agrupados por região e a soma acumulada é calculada separadamente pra cada uma.
Erro #2: Ordem errada no ORDER BY
ORDER BY dentro do OVER() controla a ordem das linhas dentro da janela. Se você errar a ordem, o resultado pode ficar bem estranho.
Você quer calcular as somas acumuladas das vendas, ordenando por mês decrescente. Dá pra fazer assim:
SELECT
month,
total,
SUM(total) OVER (ORDER BY month DESC) AS running_total
FROM
sales;
Resultado:
| month | total | running_total |
|---|---|---|
| 2023-02 | 1200 | 1200 |
| 2023-02 | 900 | 2100 |
| 2023-01 | 1000 | 3100 |
| 2023-01 | 800 | 3900 |
Parece certo, mas repara: as linhas estão agrupadas por mês, mas a soma acumulada não tá correta por causa da ordem decrescente. Por isso o resultado fica confuso.
Como corrigir: reescreve a query usando a ordem certa no ORDER BY:
SELECT
month,
total,
SUM(total) OVER (ORDER BY month ASC) AS running_total
FROM
sales;
Erro #3: Usar funções window sem índices
Funções window geralmente trabalham com muitos dados, e se não tiver índice nas colunas chave, a performance pode ir pro saco.
Exemplo: temos uma tabela large_sales com milhões de linhas, e queremos calcular o ranking das vendas:
SELECT
id,
total,
RANK() OVER (ORDER BY total DESC) AS rank
FROM
large_sales;
Com poucos dados, a query roda de boa, mas com muita linha pode demorar uma eternidade.
Como resolver: cria um índice na coluna usada no ORDER BY:
CREATE INDEX idx_total ON large_sales(total DESC);
Agora a query vai rodar bem mais rápido.
Erro #4: Não entender o que o ROWS ou RANGE faz
Quando você usa ROWS e RANGE, é importante sacar como eles definem a janela de linhas. Se não entender direito, o resultado pode ser inesperado.
Exemplo: você quer calcular a média móvel das vendas do mês atual e dos dois anteriores:
SELECT
month,
AVG(total) OVER (ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM
sales;
Se em vez de ROWS você usar RANGE:
SELECT
month,
AVG(total) OVER (ORDER BY month RANGE BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM
sales;
O resultado vai ser diferente, porque RANGE trabalha com intervalos de valores, não com quantidade de linhas.
Erro #5: Usar funções window demais
Se você mete várias funções window na mesma query, pode acabar duplicando cálculos e deixando tudo mais lento.
Exemplo:
SELECT
id,
total,
SUM(total) OVER (PARTITION BY region) AS region_total,
SUM(total) OVER (PARTITION BY region) / COUNT(total) OVER (PARTITION BY region) AS region_avg
FROM
sales;
Aqui o SUM(total) e o COUNT(total) são calculados várias vezes pra cada linha.
Como resolver: simplifica a query usando subquery ou CTE:
WITH cte_region_totals AS (
SELECT
region,
SUM(total) AS region_total,
COUNT(total) AS region_count
FROM
sales
GROUP BY
region
)
SELECT
s.id,
s.total,
t.region_total,
t.region_total / t.region_count AS region_avg
FROM
sales s
JOIN
cte_region_totals t ON s.region = t.region;
Dicas pra não vacilar
Confere o PARTITION BY e o ORDER BY: sempre checa se a janela tá definida do jeito certo.
Indexa os dados: principalmente se você usa ordenação (ORDER BY) ou faz filtro.
Usa CTE pra cálculos repetidos: isso ajuda a evitar passos duplicados.
Olha o plano de execução: usa EXPLAIN e EXPLAIN ANALYZE pra entender como o PostgreSQL tá processando sua query.
Testa com dados reais: garante que o resultado bate com o que você espera e resolve o problema certo.
GO TO FULL VERSION