Agregações e GROUP BY
📋 Visão Geral
As funções de agregação permitem realizar cálculos estatísticos sobre conjuntos de dados, como contar registos, calcular médias, somas, valores máximos e mínimos.
🔢 Funções de Agregação
COUNT - Contar Registos
" Contar todos os registos
SELECT COUNT( * )
FROM sflight
INTO @DATA(lv_total).
WRITE: / 'Total de voos:', lv_total.
" Contar valores distintos
SELECT COUNT( DISTINCT carrid )
FROM sflight
INTO @DATA(lv_companhias).
WRITE: / 'Companhias diferentes:', lv_companhias.
" Contar com condição
SELECT COUNT( * )
FROM sflight
WHERE carrid = 'LH'
INTO @DATA(lv_voos_lh).
SUM - Somar Valores
" Soma total
SELECT SUM( price )
FROM sflight
WHERE currency = 'EUR'
INTO @DATA(lv_total_preco).
WRITE: / 'Receita total:', lv_total_preco.
" Soma com várias moedas
SELECT currency,
SUM( price ) AS total
FROM sflight
GROUP BY currency
INTO TABLE @DATA(lt_totais).
LOOP AT lt_totais INTO DATA(ls_total).
WRITE: / ls_total-currency, ls_total-total.
ENDLOOP.
AVG - Calcular Média
" Média simples
SELECT AVG( price AS DEC( 15,2 ) )
FROM sflight
WHERE carrid = 'AA'
INTO @DATA(lv_preco_medio).
WRITE: / 'Preço médio:', lv_preco_medio.
" Média por companhia
SELECT carrid,
AVG( price AS DEC( 15,2 ) ) AS preco_medio
FROM sflight
GROUP BY carrid
INTO TABLE @DATA(lt_medias).
MAX e MIN - Valores Extremos
" Máximo e mínimo
SELECT MAX( price ) AS max_preco,
MIN( price ) AS min_preco
FROM sflight
INTO @DATA(ls_extremos).
WRITE: / 'Preço máximo:', ls_extremos-max_preco.
WRITE: / 'Preço mínimo:', ls_extremos-min_preco.
" Por grupo
SELECT carrid,
MAX( price ) AS max_preco,
MIN( price ) AS min_preco
FROM sflight
GROUP BY carrid
INTO TABLE @DATA(lt_ranges).
📊 GROUP BY - Agrupar Dados
Agrupamento Simples
" Agrupar por companhia
SELECT carrid,
COUNT( * ) AS num_voos
FROM sflight
GROUP BY carrid
INTO TABLE @DATA(lt_por_companhia).
LOOP AT lt_por_companhia INTO DATA(ls_comp).
WRITE: / ls_comp-carrid, ls_comp-num_voos.
ENDLOOP.
Agrupamento Múltiplo
" Agrupar por companhia e moeda
SELECT carrid,
currency,
COUNT( * ) AS quantidade,
SUM( price ) AS total,
AVG( price AS DEC( 15,2 ) ) AS media
FROM sflight
GROUP BY carrid, currency
INTO TABLE @DATA(lt_estatisticas).
LOOP AT lt_estatisticas INTO DATA(ls_stat).
WRITE: / ls_stat-carrid,
ls_stat-currency,
ls_stat-quantidade,
ls_stat-total,
ls_stat-media.
ENDLOOP.
GROUP BY com JOINs
" Estatísticas com informação de múltiplas tabelas
SELECT a~carrid,
b~carrname,
COUNT( * ) AS num_voos,
AVG( a~price AS DEC( 15,2 ) ) AS preco_medio
FROM sflight AS a
INNER JOIN scarr AS b ON a~carrid = b~carrid
GROUP BY a~carrid, b~carrname
INTO TABLE @DATA(lt_completo).
🔍 HAVING - Filtrar Grupos
Filtrar Após Agregação
" Apenas companhias com mais de 10 voos
SELECT carrid,
COUNT( * ) AS num_voos
FROM sflight
GROUP BY carrid
HAVING COUNT( * ) > 10
INTO TABLE @DATA(lt_frequentes).
" Companhias com preço médio acima de 500
SELECT carrid,
AVG( price AS DEC( 15,2 ) ) AS preco_medio
FROM sflight
GROUP BY carrid
HAVING AVG( price ) > 500
INTO TABLE @DATA(lt_premium).
HAVING vs WHERE
" WHERE filtra ANTES do GROUP BY (mais eficiente)
SELECT carrid,
COUNT( * ) AS num_voos
FROM sflight
WHERE fldate >= '20240101' " Filtra primeiro
GROUP BY carrid
HAVING COUNT( * ) > 5 " Depois filtra grupos
INTO TABLE @DATA(lt_resultado).
" Regra: Use WHERE para filtros de linhas individuais
" Use HAVING para filtros de resultados agregados
📈 Agregações Avançadas
Múltiplas Agregações
" Várias estatísticas de uma só vez
SELECT carrid,
COUNT( * ) AS total_voos,
SUM( seatsocc ) AS assentos_ocupados,
SUM( seatsmax ) AS assentos_totais,
AVG( price AS DEC( 15,2 ) ) AS preco_medio,
MAX( price ) AS preco_maximo,
MIN( price ) AS preco_minimo
FROM sflight
GROUP BY carrid
INTO TABLE @DATA(lt_analise_completa).
Cálculos Derivados
" Taxa de ocupação calculada
SELECT carrid,
SUM( seatsocc ) AS ocupados,
SUM( seatsmax ) AS total,
CAST( SUM( seatsocc ) AS DEC( 15,2 ) ) /
CAST( SUM( seatsmax ) AS DEC( 15,2 ) ) * 100
AS taxa_ocupacao
FROM sflight
GROUP BY carrid
INTO TABLE @DATA(lt_ocupacao).
LOOP AT lt_ocupacao INTO DATA(ls_ocup).
WRITE: / ls_ocup-carrid,
'Ocupação:', ls_ocup-taxa_ocupacao, '%'.
ENDLOOP.
CASE em Agregações
" Contar por categoria
SELECT carrid,
COUNT( * ) AS total_voos,
COUNT( CASE WHEN price < 500 THEN 1 END ) AS voos_economicos,
COUNT( CASE WHEN price BETWEEN 500 AND 1000 THEN 1 END ) AS voos_normais,
COUNT( CASE WHEN price > 1000 THEN 1 END ) AS voos_premium
FROM sflight
GROUP BY carrid
INTO TABLE @DATA(lt_categorias).
💡 Exemplos Práticos
Dashboard de Vendas
REPORT z_dashboard_vendas.
START-OF-SELECTION.
" Análise de vendas por mês
SELECT SUBSTRING( fldate, 1, 6 ) AS mes,
COUNT( * ) AS num_vendas,
SUM( price ) AS receita_total,
AVG( price AS DEC( 15,2 ) ) AS ticket_medio
FROM sflight
WHERE fldate >= '20240101'
GROUP BY SUBSTRING( fldate, 1, 6 )
ORDER BY mes
INTO TABLE @DATA(lt_mensal).
WRITE: / 'Dashboard Mensal'.
WRITE: / '================'.
LOOP AT lt_mensal INTO DATA(ls_mes).
WRITE: / ls_mes-mes,
'Vendas:', ls_mes-num_vendas,
'Receita:', ls_mes-receita_total,
'Ticket Médio:', ls_mes-ticket_medio.
ENDLOOP.
Top Companhias
METHOD obter_top_companhias.
" Top 10 companhias por receita
SELECT TOP 10
a~carrid,
b~carrname,
COUNT( * ) AS num_voos,
SUM( a~price ) AS receita_total
FROM sflight AS a
INNER JOIN scarr AS b ON a~carrid = b~carrid
GROUP BY a~carrid, b~carrname
ORDER BY receita_total DESCENDING
INTO TABLE @DATA(lt_top).
RETURN lt_top.
ENDMETHOD.
Análise de Tendências
" Comparar performance ano a ano
SELECT SUBSTRING( fldate, 1, 4 ) AS ano,
carrid,
COUNT( * ) AS voos,
AVG( seatsocc AS DEC( 15,2 ) ) AS ocupacao_media
FROM sflight
GROUP BY SUBSTRING( fldate, 1, 4 ), carrid
HAVING COUNT( * ) > 50
ORDER BY ano, carrid
INTO TABLE @DATA(lt_tendencias).
🎯 Boas Práticas
✅ Fazer
" 1. Especificar todos os campos no GROUP BY
SELECT carrid, connid, COUNT( * )
FROM sflight
GROUP BY carrid, connid " ✅ Correto
INTO TABLE @DATA(lt_data).
" 2. Usar aliases para melhor legibilidade
SELECT carrid,
COUNT( * ) AS total_voos,
AVG( price AS DEC( 15,2 ) ) AS preco_medio " ✅
FROM sflight
GROUP BY carrid
INTO TABLE @DATA(lt_stats).
" 3. Combinar WHERE e HAVING adequadamente
SELECT carrid, COUNT( * ) AS num
FROM sflight
WHERE fldate >= '20240101' " ✅ Filtra antes (mais rápido)
GROUP BY carrid
HAVING COUNT( * ) > 10 " ✅ Filtra resultado agregado
INTO TABLE @DATA(lt_result).
❌ Evitar
" 1. Campos não agregados sem GROUP BY
SELECT carrid, connid, COUNT( * ) " ❌ connid não está no GROUP BY
FROM sflight
GROUP BY carrid
INTO TABLE @DATA(lt_data).
" 2. SELECT * com GROUP BY
SELECT *, COUNT( * ) " ❌ Ambíguo
FROM sflight
GROUP BY carrid
INTO TABLE @DATA(lt_data).
" 3. Agregações desnecessárias
SELECT carrid, COUNT( * )
FROM sflight
GROUP BY carrid
HAVING carrid = 'LH' " ❌ Deve estar no WHERE
INTO TABLE @DATA(lt_data).
" Correto:
SELECT carrid, COUNT( * )
FROM sflight
WHERE carrid = 'LH' " ✅
GROUP BY carrid
INTO TABLE @DATA(lt_data).
📊 Performance
Otimizações
- Use índices no WHERE: Filtre antes de agrupar
- Limite resultados: Use
UP TO n ROWSquando possível - Evite HAVING complexos: Use WHERE sempre que possível
- Agregações específicas: Não use
COUNT(*)se pode usarCOUNT(campo_indexado)
" ❌ Lento
SELECT carrid, COUNT( * )
FROM sflight
GROUP BY carrid
HAVING carrid IN ('AA', 'LH', 'BA')
INTO TABLE @DATA(lt_slow).
" ✅ Rápido
SELECT carrid, COUNT( * )
FROM sflight
WHERE carrid IN ('AA', 'LH', 'BA') " Usa índice
GROUP BY carrid
INTO TABLE @DATA(lt_fast).
🔗 Próximos Passos
- INSERT UPDATE DELETE - Manipular dados
- WHERE Dinâmico - Consultas flexíveis
- Otimizações SQL - Performance avançada
Tags: #SQL #Agregações #GROUP-BY #HAVING #COUNT #SUM #AVG