Domine Funções de Agregação SQL: Guia de GROUP BY, HAVING e Window Functions

As funções de agregação como SUM() ou COUNT() são o pão com manteiga de qualquer análise de dados. Contudo, rapidamente percebemos que elas são insuficientes para perguntas mais complexas. Se quer realmente extrair informações valiosas e ir além do básico, precisa de dominar as Funções de Agregação SQL: GROUP BY, HAVING e Window Functions. Este guia prático foi desenhado para o levar do nível iniciante ao avançado, transformando a forma como interage com os seus dados.

Neste artigo, vamos desmistificar estas ferramentas poderosas. Primeiro, vamos mostrar-lhe como organizar os seus dados em grupos lógicos com a cláusula GROUP BY. Em seguida, aprenderá a filtrar esses grupos com HAVING, uma capacidade que o WHERE simplesmente não tem. Finalmente, faremos um mergulho profundo nas Window Functions, a ferramenta que lhe permite fazer cálculos complexos, como rankings e médias móveis, sem perder o detalhe de cada registo individual. Prepare-se para elevar o seu SQL a um novo patamar.

A Base de Tudo: Recapitulação Rápida das Funções de Agregação

Antes de avançarmos, vamos recordar rapidamente o que são as funções de agregação. Em termos simples, estas funções operam sobre um conjunto de valores para devolver um único valor de resumo. São ferramentas essenciais para obter uma visão geral dos seus dados. As mais comuns são, sem dúvida, COUNT() para contar registos, SUM() para somar valores e AVG() para calcular a média.

Imagine que temos uma tabela simples de Vendas com as colunas ID_Produto, Valor e Data. Se quiséssemos saber o total de vendas, usaríamos um comando simples como este:

SELECT SUM(Valor) FROM Vendas;

O resultado seria um único número: o total de todas as vendas. Aqui reside a sua maior força e também a sua principal limitação. As funções de agregação, por si só, colapsam todos os registos num único resultado. Mas e se a sua pergunta for: “Qual é o total de vendas por cada produto?”. É exatamente para resolver este problema que a cláusula GROUP BY foi criada.

Aprofundar o GROUP BY: A Arte de Agrupar Dados

A cláusula GROUP BY é a sua ferramenta principal para segmentar dados antes de aplicar funções de agregação. Em vez de calcular a soma de todas as vendas, o GROUP BY permite-lhe dividir a tabela em grupos mais pequenos e aplicar a função a cada um desses grupos de forma independente. Pense nisto como criar “sub-tabelas” virtuais para cada valor único na coluna que especificar.

Sintaxe e Ordem de Execução

É crucial entender onde o GROUP BY se encaixa na ordem de execução de uma consulta SQL. A ordem lógica é: FROM, WHERE, GROUP BY, SELECT, e finalmente ORDER BY. Isto significa que o SQL primeiro filtra os registos com a cláusula WHERE e só depois agrupa os resultados. Qualquer coluna no seu SELECT deve estar ou numa função de agregação ou na cláusula GROUP BY.

Exemplo Prático 1: Agrupar por uma coluna

Vamos responder à pergunta anterior: calcular o total de vendas por cada produto. Com o GROUP BY, a tarefa torna-se bastante simples. Usamos o comando para agrupar dados em SQL de forma eficaz.

SELECT ID_Produto, SUM(Valor) AS TotalVendas FROM Vendas GROUP BY ID_Produto;

O resultado já não é um único número. Em vez disso, obterá uma lista de cada ID_Produto com o seu total de vendas correspondente ao lado. O GROUP BY ID_Produto instruiu o SQL a criar um grupo para cada produto distinto e a calcular a soma do Valor apenas para os registos dentro desse grupo.

Exemplo Prático 2: Agrupar por múltiplas colunas

Podemos levar isto ainda mais longe. E se quisermos ver as vendas totais por produto e por mês? Basta adicionar a segunda coluna à cláusula GROUP BY. Isto cria grupos mais granulares, baseados na combinação única das colunas especificadas.

SELECT ID_Produto, STRFTIME('%Y-%m', Data) AS MesVenda, SUM(Valor) AS TotalVendasMensal FROM Vendas GROUP BY ID_Produto, MesVenda;

Agora, o resultado mostrará o total de vendas para cada produto em cada mês específico em que houve vendas. Esta capacidade de agrupar por múltiplos critérios é fundamental para análises detalhadas.

Filtrar Grupos com HAVING: O `WHERE` para os Agregados

Depois de agrupar os seus dados com sucesso, o próximo passo lógico é filtrá-los. Por exemplo, pode querer ver apenas os produtos cujas vendas totais ultrapassam um determinado valor. A sua primeira intuição poderia ser usar a cláusula WHERE, mas isso resultaria num erro. Porquê? Porque o WHERE é executado *antes* do GROUP BY.

A Diferença Crucial: `WHERE` vs. `HAVING`

Compreender a diferença entre WHERE e HAVING é um marco no seu aprendizado de SQL. É bastante simples quando se percebe a ordem de operações:

  • WHERE: Filtra registos individuais. Opera sobre os dados brutos da tabela antes de qualquer agrupamento ser feito.
  • HAVING: Filtra grupos inteiros. Opera sobre os resultados das funções de agregação depois de os dados terem sido agrupados pelo GROUP BY.

Em resumo, use WHERE para filtrar linhas e HAVING para filtrar grupos. Esta é a regra de ouro para filtrar grupos com HAVING.

Sintaxe e Ordem de Execução

A cláusula HAVING é colocada imediatamente após o GROUP BY. A nova ordem de execução lógica é: FROM, WHERE, GROUP BY, HAVING, SELECT, ORDER BY. O HAVING tem acesso aos resultados agregados, o que o torna perfeito para a nossa tarefa.

Exemplo Prático

Vamos resolver o nosso problema: listar apenas os produtos cujo total de vendas foi superior a 1000€. Usando o HAVING, a consulta fica assim:

SELECT ID_Produto, SUM(Valor) AS TotalVendas FROM Vendas GROUP BY ID_Produto HAVING SUM(Valor) > 1000;

Este comando primeiro agrupa todas as vendas por ID_Produto e calcula a sua soma. Depois, a cláusula HAVING avalia cada grupo e descarta todos aqueles cujo TotalVendas não seja superior a 1000. O resultado final é uma lista limpa apenas com os produtos mais vendidos.

O Salto Quântico: Introdução às Window Functions

Até agora, vimos como o GROUP BY é excelente para agregar dados, mas ele tem uma desvantagem significativa: colapsa os registos originais. E se precisasse de ver cada venda individual e, na mesma linha, o total de vendas do produto a que essa venda pertence? Com GROUP BY, isto é impossível. Perde-se o detalhe individual.

É aqui que entram as Window Functions (ou funções de janela). O que são funções de janela? São funções que realizam um cálculo sobre um conjunto de registos relacionados com o registo atual, mas fazem-no sem agrupar o resultado final. Isto significa que mantém todos os seus registos originais e simplesmente adiciona uma nova coluna com o resultado do cálculo da “janela”.

A analogia perfeita é imaginar que está a olhar para um registo da sua tabela através de uma janela. Essa janela não só lhe mostra o registo atual, como também lhe dá uma visão dos registos “vizinhos” (como o total do grupo, a média, o registo anterior, etc.), permitindo uma análise de dados com SQL muito mais rica.

Anatomia de uma Window Function: `OVER`, `PARTITION BY` e `ORDER BY`

A sintaxe de uma Window Function pode parecer intimidante no início, mas é bastante lógica quando a decompomos. A magia acontece dentro da cláusula OVER(), que define a “janela” de dados sobre a qual a função irá operar. A estrutura geral é:

FUNÇÃO_DE_JANELA() OVER (PARTITION BY coluna(s) ORDER BY coluna(s))

A Cláusula `OVER()`

A presença de OVER() é o que transforma uma função de agregação normal numa Window Function. Se a deixar vazia, a janela abrange a tabela inteira. Por exemplo, para ver o valor de cada venda ao lado do total geral de vendas:

SELECT Valor, SUM(Valor) OVER () AS TotalGeral FROM Vendas;

`PARTITION BY`: O `GROUP BY` das Window Functions

A parte mais poderosa é o PARTITION BY. Esta cláusula divide os dados em partições ou janelas, e a função é calculada de forma independente para cada partição. Funciona como o GROUP BY, mas sem colapsar os registos. Para saber como usar PARTITION BY em SQL, veja este exemplo que resolve o nosso problema anterior:

SELECT Valor, ID_Produto, SUM(Valor) OVER (PARTITION BY ID_Produto) AS TotalPorProduto FROM Vendas;

O resultado mostrará cada registo de venda. Ao lado, a coluna TotalPorProduto exibirá a soma total das vendas para o ID_Produto daquela linha específica, repetindo esse valor para todos os registos do mesmo produto.

`ORDER BY` (dentro de `OVER`)

A cláusula ORDER BY dentro de OVER() define a ordem dos registos dentro de cada partição. Embora pareça simples, é fundamental para funções de ranking e cálculos cumulativos, como médias móveis. A ordem aqui afeta diretamente o resultado do cálculo, linha a linha.

Casos de Uso Práticos: O Poder das Window Functions em Ação

Agora que entendemos a teoria, vamos ver alguns exemplos práticos de Window Functions. É aqui que o seu verdadeiro poder para análise de dados complexa se revela.

Caso 1: Rankings (`ROW_NUMBER`, `RANK`, `DENSE_RANK`)

Uma das tarefas mais comuns é criar ranking com SQL. As Window Functions tornam isto trivial. Suponha que queremos classificar as vendas por valor dentro de cada categoria de produto.

  • ROW_NUMBER(): Atribui um número sequencial único a cada registo.
  • RANK(): Atribui o mesmo rank a valores empatados, mas salta a próxima posição.
  • DENSE_RANK(): Atribui o mesmo rank a empates, mas não salta posições.
SELECT Valor, Categoria, RANK() OVER (PARTITION BY Categoria ORDER BY Valor DESC) AS RankingVendas FROM Vendas;

Este comando irá criar um ranking de vendas separado para cada categoria, permitindo-lhe ver facilmente os produtos de topo em cada segmento.

Caso 2: Percentagem do Total

Outro caso de uso poderoso é calcular a contribuição de cada registo para o total da sua partição. Por exemplo, qual a percentagem que cada venda representa do total de vendas da sua categoria?

SELECT Valor, Categoria, (Valor * 100.0 / SUM(Valor) OVER (PARTITION BY Categoria)) AS PercentagemDaCategoria FROM Vendas;

Esta consulta devolve cada venda individual juntamente com uma nova coluna que mostra o seu peso percentual dentro da categoria, uma métrica valiosa para análise de desempenho.

Caso 3: Médias Móveis (Avançado)

Para análises de séries temporais, calcular médias móveis com SQL é uma técnica comum para suavizar flutuações. As Window Functions permitem definir uma “janela” de registos (ex: os 2 anteriores e o atual) para o cálculo.

SELECT Data, Valor, AVG(Valor) OVER (ORDER BY Data ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS MediaMovel3Dias FROM Vendas;

Este comando calcula, para cada dia, a média do valor das vendas desse dia e dos dois dias anteriores, fornecendo uma visão clara da tendência de vendas.

Conclusão: De Analista a Estratega com SQL

Chegámos ao fim da nossa jornada. Vimos como o GROUP BY nos permite agregar dados de forma inteligente e como o HAVING nos dá o poder de filtrar esses agregados. Mais importante, desvendámos o poder das Funções de Agregação SQL: GROUP BY, HAVING e Window Functions, que abrem um novo mundo de possibilidades analíticas sem sacrificar o detalhe dos dados.

Dominar estas três ferramentas é o que separa uma análise de dados superficial de uma análise profunda e estratégica. Elas permitem-lhe responder a perguntas de negócio muito mais complexas, desde a identificação de segmentos de alto desempenho até à análise de tendências ao longo do tempo. Com estas competências, deixa de ser apenas alguém que extrai dados para se tornar alguém que gera insights valiosos.

A melhor forma de solidificar este conhecimento é praticar. Pegue nos seus próprios conjuntos de dados e comece a experimentar. Desafie-se a responder a novas perguntas usando estas técnicas. Deixe um comentário abaixo com as suas dúvidas ou partilhe um caso de uso interessante que tenha descoberto!

Pode fazer uma doação para ajudar a mater o site, Obrigado!