1. Introducción al Análisis de Agregados
La operación de agregación es un concepto muy importante en las consultas a bases de datos. Normalmente se utiliza para análisis estadísticos como sumas, conteos, promedios, valores máximos y mínimos. Estas operaciones ayudan a los usuarios a extraer información significativa de una gran cantidad de datos, brindando soporte para el análisis de datos y la toma de decisiones. Las funciones implementadas en la base de datos para la agregación generalmente se conocen como funciones de agregado.
2. Operaciones de Agregación Básicas
2.1 Concepto de Funciones de Agregado
Las funciones de agregado son funciones utilizadas en lenguajes de consulta de bases de datos para realizar una serie de cálculos y devolver un único valor. En SQL y lenguajes de consulta similares, las funciones de agregado pueden operar en una columna de datos y devolver un único valor, como suma (SUM), promedio (AVG), conteo (COUNT), máximo (MAX) y mínimo (MIN). Cuando necesitamos realizar análisis estadísticos de datos, las funciones de agregado son herramientas importantes para procesar conjuntos de datos y extraer datos estadísticos.
2.2 Agregación de un Solo Campo
En aplicaciones prácticas, el análisis de agregación de un solo campo es un requisito muy común, a menudo utilizado para obtener resultados estadísticos como la suma, el promedio, el máximo y el mínimo de una columna específica. Supongamos que tenemos una tabla de información de pagos y queremos calcular el monto total pagado por los usuarios. Usando el marco ent
, podemos construir una consulta desde la entidad y aplicar funciones de agregado de la siguiente manera:
func Do(ctx context.Context, client *ent.Client) {
// Calcular la suma del campo Monto para la entidad Pago
suma, err := client.Payment.Query().
Aggregate(
ent.Sum(payment.Amount),
).
Int(ctx)
if err != nil {
log.Fatalf("Error al obtener la suma: %v", err)
}
log.Printf("Monto total de los pagos: %d", suma)
}
En el fragmento de código anterior, iniciamos una consulta para la entidad de pago utilizando client.Payment.Query()
, luego utilizamos el método Aggregate()
para llamar a la función ent.Sum
con payment.Amount
como parámetro para calcular el monto total de los pagos. Usamos .Int(ctx)
para convertir el resultado de la agregación a un número entero y lo registramos.
2.3 Agregación de Múltiples Campos
En muchos casos, necesitamos realizar operaciones de agregación en múltiples campos en lugar de solo uno. En esta sección, demostraremos cómo lograr la agregación de múltiples campos mediante un ejemplo.
En este ejemplo, sumaremos, encontraremos el mínimo, encontraremos el máximo y contaremos el campo Edad
en la tabla de mascotas.
func Do(ctx context.Context, client *ent.Client) {
var v []struct {
Suma, Min, Max, Cont int
}
err := client.Pet.Query().
Aggregate(
ent.Sum(pet.FieldAge), // Suma de Edad
ent.Min(pet.FieldAge), // Edad Mínima
ent.Max(pet.FieldAge), // Edad Máxima
ent.Count(), // Conteo
).
Scan(ctx, &v)
if err != nil {
log.Fatalf("Consulta fallida: %v", err)
}
// Mostrar todos los resultados de la agregación
for _, agg := range v {
fmt.Printf("Suma: %d Mínimo: %d Máximo: %d Conteo: %d\n", agg.Suma, agg.Min, agg.Max, agg.Cont)
}
}
En el código anterior, utilizamos la función Aggregate
para realizar la agregación de múltiples campos y la función Scan
para almacenar los resultados de la agregación en la matriz v
. Luego, iteramos a través de v
para mostrar todos los resultados de la agregación.
3. Aplicación de la Agregación con Group By
3.1. Uso de Group By para Agrupar Campos
En las operaciones de bases de datos, Group By
es un método común para agrupar datos. En esta sección, aprenderemos cómo utilizar Group By
para agrupar datos en la base de datos.
Ejemplo tutorial, cómo agrupar uno o más campos usando Group By.
Supongamos que tenemos una tabla de usuarios y necesitamos agrupar el campo nombre
de los usuarios y calcular el número de usuarios en cada grupo. A continuación se muestra un ejemplo de código de cómo lograr este requisito:
func Do(ctx context.Context, client *ent.Client) {
nombres, err := client.User.
Query().
GroupBy(user.FieldName).
Strings(ctx)
if err != nil {
log.Fatalf("Fallo al ejecutar la consulta agrupada: %v", err)
}
// Mostrar el nombre de cada grupo
for _, nombre := range nombres {
fmt.Println("Nombre del grupo:", nombre)
}
}
En el código anterior, utilizamos el método GroupBy
del generador de consultas para especificar por qué campo queremos agrupar.
3.2. Agrupación y agregación de varios campos
A veces, queremos agrupar datos basados en varios campos y realizar funciones de agregación en cada grupo. A continuación se muestra un ejemplo de cómo lograr este requisito:
El siguiente código muestra cómo agrupar datos en la tabla de usuarios basándose en los campos nombre
y edad
, y calcular la suma total de edades y el número de usuarios en cada grupo.
func Do(ctx context.Context, client *ent.Client) {
var v []struct {
Nombre string `json:"nombre"`
Edad int `json:"edad"`
Suma int `json:"suma"`
Conteo int `json:"conteo"`
}
err := client.User.Query().
GroupBy(user.FieldName, user.FieldAge).
Aggregate(ent.Count(), ent.Sum(user.FieldAge)).
Scan(ctx, &v)
if err != nil {
log.Fatalf("Error al ejecutar la consulta de agrupación y agregación de múltiples campos: %v", err)
}
// Mostrar información detallada para cada grupo
for _, grupo := range v {
fmt.Printf("Nombre: %s Edad: %d Suma: %d Conteo: %d\n", grupo.Nombre, grupo.Edad, grupo.Suma, grupo.Conteo)
}
}
En este ejemplo, no solo agrupamos los datos basados en los campos nombre
y edad
en la tabla de usuarios, sino que también usamos las funciones de agregación Count
y Sum
para calcular el número total de registros y la suma total de edades en cada grupo.
4. Combinando el Having con Group By
La cláusula Having
filtra los resultados de agregación obtenidos después de la operación Group By
. El siguiente ejemplo muestra cómo seleccionar solo los usuarios con la edad máxima en cada rol:
func Do(ctx context.Context, client *ent.Client) {
var usuarios []struct {
Id Int
Edad Int
Rol string
}
err := client.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("Error al ejecutar la consulta combinando Having con Group By: %v", err)
}
// Mostrar información de usuario que satisface la condición Having
for _, usuario := range usuarios {
fmt.Printf("ID: %d Edad: %d Rol: %s\n", usuario.Id, usuario.Edad, usuario.Rol)
}
}
El código anterior generará una consulta SQL equivalente para seleccionar los usuarios con la edad máxima en cada rol.