kb.erickguedes.com
SQL: Domínio de Bancos Relacionais

Agregação e Agrupamento

Aula 3 de 8

Funções de Agregação

SELECT 
    COUNT(*) AS total_linhas,
    COUNT(email) AS emails_nao_nulos,       -- ignora NULL
    COUNT(DISTINCT cidade) AS cidades_unicas,
    SUM(salario) AS folha_pagamento,
    AVG(salario) AS media_salarial,
    MIN(salario) AS menor_salario,
    MAX(salario) AS maior_salario
FROM funcionarios;

Comportamento com NULL

-- AVG ignora NULL (soma/não-nulos)
-- SUM ignora NULL (NULL + 10 = NULL? Não, SUM trata NULL como 0)
-- COUNT(coluna) ignora NULL
-- COUNT(*) conta linhas (inclui NULLs)

-- Tratar NULL explicitamente
SELECT AVG(COALESCE(comissao, 0)) FROM funcionarios;

GROUP BY

-- Agrupar por departamento
SELECT 
    departamento,
    COUNT(*) AS funcionarios,
    AVG(salario)::DECIMAL(10,2) AS media_salarial,
    SUM(salario) AS total
FROM funcionarios
GROUP BY departamento
ORDER BY media_salarial DESC;

-- Múltiplas colunas
SELECT 
    departamento,
    cargo,
    COUNT(*) AS qtd,
    AVG(salario) AS media
FROM funcionarios
GROUP BY departamento, cargo
ORDER BY departamento, cargo;

HAVING — Filtro de Grupos

-- WHERE filta linhas, HAVING filta grupos
SELECT 
    cliente_id,
    COUNT(*) AS total_pedidos,
    SUM(total) AS total_gasto
FROM pedidos
WHERE data_pedido >= '2024-01-01'
GROUP BY cliente_id
HAVING COUNT(*) >= 5 AND SUM(total) > 1000
ORDER BY total_gasto DESC;

Window Functions

-- ROW_NUMBER: numeração dentro de partição
SELECT 
    nome,
    departamento,
    salario,
    ROW_NUMBER() OVER (PARTITION BY departamento ORDER BY salario DESC) AS rank_salario
FROM funcionarios;

-- RANK vs DENSE_RANK vs ROW_NUMBER
SELECT 
    nome, salario,
    ROW_NUMBER() OVER (ORDER BY salario DESC) AS "rn",
    RANK() OVER (ORDER BY salario DESC) AS "rank",
    DENSE_RANK() OVER (ORDER BY salario DESC) AS "dense"
FROM funcionarios;
-- ROW_NUMBER: 1, 2, 3, 4 (sem empates)
-- RANK: 1, 1, 3, 4 (empate pula)
-- DENSE_RANK: 1, 1, 2, 3 (empate não pula)

-- LAG/LEAD (linha anterior/próxima)
SELECT 
    data,
    valor,
    LAG(valor) OVER (ORDER BY data) AS valor_anterior,
    valor - LAG(valor) OVER (ORDER BY data) AS variacao,
    AVG(valor) OVER (ORDER BY data ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS media_7dias
FROM cotacoes;

-- SUM acumulado
SELECT 
    mes,
    receita,
    SUM(receita) OVER (ORDER BY mes) AS receita_acumulada
FROM receitas_mensais;

-- FIRST_VALUE / LAST_VALUE
SELECT 
    produto,
    preco,
    FIRST_VALUE(produto) OVER (ORDER BY preco DESC) AS mais_caro
FROM produtos;

Funções Estatísticas

-- PERCENTILE (mediana, quartis)
SELECT 
    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salario) AS mediana,
    PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY salario) AS q1,
    PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY salario) AS q3
FROM funcionarios;

-- Moda (valor mais frequente)
SELECT salario, COUNT(*) AS freq
FROM funcionarios
GROUP BY salario
ORDER BY freq DESC
LIMIT 1;

Lab: Análise de Vendas

SELECT 
    DATE_TRUNC('month', data_pedido) AS mes,
    COUNT(DISTINCT cliente_id) AS clientes_ativos,
    COUNT(id) AS pedidos,
    SUM(total) AS receita,
    AVG(total) AS ticket_medio,
    LAG(SUM(total)) OVER (ORDER BY DATE_TRUNC('month', data_pedido)) AS receita_mes_anterior,
    SUM(total) - LAG(SUM(total)) OVER (ORDER BY DATE_TRUNC('month', data_pedido)) AS variacao
FROM pedidos
GROUP BY mes
ORDER BY mes;

Funções de agregação + GROUP BY = análise de dados. Window Functions (OVER) para análises avançadas sem perder detalhes. HAVING filtra grupos como WHERE filtra linhas.