1. Introdução à Análise de Agregação
A operação de agregação é um conceito muito importante em consultas de banco de dados. Geralmente é usada para análises estatísticas, como soma, contagem, média, valores máximos e mínimos. Essas operações ajudam os usuários a extrair informações significativas de uma grande quantidade de dados, fornecendo suporte para análise de dados e tomada de decisões. As funções implementadas no banco de dados para agregação são geralmente referidas como funções de agregação.
2. Operações de Agregação Básicas
2.1 Conceito de Funções de Agregação
Funções de agregação são funções utilizadas em linguagens de consulta de banco de dados para realizar uma série de cálculos e retornar um único valor. Em SQL e linguagens de consulta similares, as funções de agregação podem operar em uma coluna de dados e retornar um único valor, como soma (SUM), média (AVG), contagem (COUNT), máximo (MAX) e mínimo (MIN). Quando precisamos realizar análises estatísticas de dados, as funções de agregação são ferramentas importantes para processar conjuntos de dados e extrair dados estatísticos.
2.2 Agregação de Campo Único
Nas aplicações práticas, a análise de agregação de campo único é um requisito muito comum, frequentemente utilizado para obter resultados estatísticos, como a soma, média, valor máximo e mínimo de uma coluna específica. Suponha que tenhamos uma tabela de informações de pagamentos e desejamos calcular o valor total pago pelos usuários. Usando o framework ent
, podemos construir uma consulta a partir da entidade e aplicar funções de agregação da seguinte forma:
func Executar(ctx context.Context, cliente *ent.Client) {
// Calcular a soma do campo Amount para a entidade de Pagamento
soma, err := cliente.Payment.Query().
Aggregate(
ent.Sum(payment.Amount),
).
Int(ctx)
if err != nil {
log.Fatalf("Falha ao obter a soma: %v", err)
}
log.Printf("Valor total dos pagamentos: %d", soma)
}
No trecho de código acima, iniciamos uma consulta para a entidade de pagamento usando cliente.Payment.Query()
, em seguida, utilizamos o método Aggregate()
para chamar a função ent.Sum
com payment.Amount
como parâmetro para calcular o valor total dos pagamentos. Usamos .Int(ctx)
para converter o resultado da agregação em um inteiro e registrá-lo.
2.3 Agregação de Múltiplos Campos
Em muitos casos, precisamos realizar operações de agregação em múltiplos campos em vez de apenas um. Nesta seção, demonstraremos como alcançar a agregação de múltiplos campos por meio de um exemplo.
Neste exemplo, iremos somar, encontrar o mínimo, encontrar o máximo e contar o campo Age
na tabela de animais de estimação.
func Executar(ctx context.Context, cliente *ent.Client) {
var v []struct {
Soma, Min, Max, Contagem int
}
err := cliente.Pet.Query().
Aggregate(
ent.Sum(pet.FieldAge), // Soma da Idade
ent.Min(pet.FieldAge), // Idade Mínima
ent.Max(pet.FieldAge), // Idade Máxima
ent.Count(), // Contagem
).
Scan(ctx, &v)
if err != nil {
log.Fatalf("Consulta falhou: %v", err)
}
// Exibir todos os resultados agregados
for _, agg := range v {
fmt.Printf("Soma: %d Mínimo: %d Máximo: %d Contagem: %d\n", agg.Soma, agg.Min, agg.Max, agg.Contagem)
}
}
No código acima, utilizamos a função Aggregate
para realizar a agregação de vários campos e a função Scan
para armazenar os resultados da agregação na slice v
. Em seguida, iteramos através de v
para exibir todos os resultados agregados.
3. Aplicação do Group By na Agregação
3.1. Utilizando Group By para Agrupar Campos
Nas operações de banco de dados, o Group By
é um método comum para agrupar dados. Nesta seção, aprenderemos como usar o Group By
para agrupar dados no banco de dados.
Exemplo de tutorial, como agrupar um ou mais campos usando o Group By.
Supondo que tenhamos uma tabela de usuários e precisamos agrupar o campo nome
dos usuários e calcular o número de usuários em cada grupo. Abaixo está um exemplo de código de como atender a este requisito:
func Executar(ctx context.Context, cliente *ent.Client) {
nomes, err := cliente.User.
Query().
GroupBy(user.FieldName).
Strings(ctx)
if err != nil {
log.Fatalf("Falha ao executar a consulta agrupada: %v", err)
}
// Exibir o nome de cada grupo
for _, nome := range nomes {
fmt.Println("Nome do grupo:", nome)
}
}
No código acima, utilizamos o método GroupBy
do construtor de consulta para especificar por qual campo queremos agrupar.
3.2. Agrupamento e Agregação de Múltiplos Campos
Às vezes, queremos agrupar dados com base em múltiplos campos e realizar funções de agregação em cada grupo. Abaixo está um exemplo de como alcançar esse requisito:
O código a seguir demonstra como agrupar dados na tabela de usuário com base nos campos name
e age
, e calcular a idade total e o número de usuários em cada grupo.
func Fazer(ctx context.Context, cliente *ent.Cliente) {
var v []struct {
Nome string `json:"name"`
Idade int `json:"age"`
Soma int `json:"sum"`
Contagem int `json:"count"`
}
err := cliente.User.Query().
GroupBy(user.FieldName, user.FieldAge).
Aggregate(ent.Count(), ent.Sum(user.FieldAge)).
Scan(ctx, &v)
if err != nil {
log.Fatalf("Falha ao executar a consulta de agrupamento e agregação de múltiplos campos: %v", err)
}
// Saída de informações detalhadas para cada grupo
for _, grupo := range v {
fmt.Printf("Nome: %s Idade: %d Soma: %d Contagem: %d\n", grupo.Nome, grupo.Idade, grupo.Soma, grupo.Contagem)
}
}
Neste exemplo, não apenas agrupamos os dados com base nos campos name
e age
na tabela de usuário, mas também usamos as funções de agregação Count
e Sum
para calcular o número total de registros e a idade total em cada grupo.
4. Combinando Having com Group By
A cláusula Having
filtra os resultados de agregação obtidos após a operação Group By
. O exemplo a seguir mostra como selecionar apenas os usuários com a idade máxima em cada cargo:
func Fazer(ctx context.Context, cliente *ent.Cliente) {
var usuarios []struct {
Id Int
Idade Int
Cargo string
}
err := cliente.User.Query().
Modify(func(s *sql.Selector) {
s.GroupBy(user.FieldRole)
s.Having(
sql.EQ(
user.FieldAge,
sql.Raw(sql.Max(user.FieldAge)),
),
)
}).
ScanX(ctx, &usuarios)
if err != nil {
log.Fatalf("Falha ao executar a consulta combinando Having com Group By: %v", err)
}
// Saída das informações do usuário que satisfazem a condição Having
for _, usuario := range usuarios {
fmt.Printf("ID: %d Idade: %d Cargo: %s\n", usuario.Id, usuario.Idade, usuario.Cargo)
}
}
O código acima irá gerar uma consulta SQL equivalente para selecionar os usuários com a idade máxima em cada cargo.