1. Introduzione all'analisi aggregata

L'operazione di aggregazione è un concetto molto importante nelle interrogazioni dei database. Viene solitamente utilizzata per l'analisi statistica, come somma, conteggio, media, valori massimi e minimi. Queste operazioni aiutano gli utenti a estrarre informazioni significative da una grande quantità di dati, fornendo supporto per l'analisi dei dati e la presa di decisioni. Le funzioni implementate nel database per l'aggregazione sono generalmente indicate come funzioni di aggregazione.

2. Operazioni di Aggregazione di Base

2.1 Concetto delle Funzioni di Aggregazione

Le funzioni di aggregazione sono funzioni utilizzate nei linguaggi di interrogazione dei database per eseguire una serie di calcoli e restituire un singolo valore. In SQL e linguaggi di interrogazione simili, le funzioni di aggregazione possono operare su una colonna di dati e restituire un singolo valore, come somma (SUM), media (AVG), conteggio (COUNT), massimo (MAX) e minimo (MIN). Quando è necessario eseguire un'analisi statistica dei dati, le funzioni di aggregazione sono strumenti importanti per l'elaborazione di set di dati ed l'estrazione di dati statistici.

2.2 Aggregazione su Singolo Campo

Nelle applicazioni pratiche, l'analisi aggregata su singolo campo è un requisito molto comune, spesso utilizzato per ottenere risultati statistici come somma, media, massimo e minimo di una colonna particolare. Supponiamo di avere una tabella di informazioni sui pagamenti e vogliamo calcolare l'importo totale pagato dagli utenti. Utilizzando il framework ent, possiamo costruire una query dall'entità e applicare le funzioni di aggregazione come segue:

func Esegui(ctx context.Context, client *ent.Client) {
    // Calcola la somma del campo Amount per l'entità Payment
    somma, err := client.Payment.Query().
        Aggregate(
            ent.Sum(payment.Amount),
        ).
        Int(ctx)
    if err != nil {
        log.Fatalf("Impossibile ottenere la somma: %v", err)
    }
    log.Printf("Importo totale dei pagamenti: %d", somma)
}

Nel frammento di codice sopra, avviamo una query per l'entità di pagamento utilizzando client.Payment.Query(), quindi utilizziamo il metodo Aggregate() per chiamare la funzione ent.Sum con payment.Amount come parametro per calcolare l'importo totale dei pagamenti. Usiamo .Int(ctx) per convertire il risultato dell'aggregazione in un intero e lo registriamo.

2.3 Aggregazione su Più Campi

In molti casi, è necessario eseguire operazioni di aggregazione su più campi anziché uno solo. In questa sezione, illustreremo come ottenere un'aggregazione su più campi attraverso un esempio.

In questo esempio, sommeremo, troveremo il minimo, il massimo e conteremo il campo Age nella tabella dei pet.

func Esegui(ctx context.Context, client *ent.Client) {
    var v []struct {
        Somma, Minimo, Massimo, Conteggio int
    }
    err := client.Pet.Query().
        Aggregate(
            ent.Sum(pet.FieldAge), // Somma di Age
            ent.Min(pet.FieldAge), // Età minima
            ent.Max(pet.FieldAge), // Età massima
            ent.Count(),           // Conteggio
        ).
        Scan(ctx, &v)
    if err != nil {
        log.Fatalf("Interrogazione fallita: %v", err)
    }
    // Output di tutti i risultati dell'aggregazione
    for _, agg := range v {
        fmt.Printf("Somma: %d Minimo: %d Massimo: %d Conteggio: %d\n", agg.Somma, agg.Minimo, agg.Massimo, agg.Conteggio)
    }
}

Nel codice sopra, utilizziamo la funzione Aggregate per eseguire un'aggregazione su più campi, e utilizziamo la funzione Scan per memorizzare i risultati dell'aggregazione nella slice v. Quindi, iteriamo attraverso v per visualizzare tutti i risultati dell'aggregazione.

3. Applicazione dell'Aggregazione con Group By

3.1 Utilizzo di Group By per Raggruppare i Campi

Nelle operazioni dei database, Group By è un metodo comune per raggruppare i dati. In questa sezione, impareremo come utilizzare Group By per raggruppare i dati nel database.

Esempio di tutorial, come raggruppare uno o più campi utilizzando Group By.

Supponiamo di avere una tabella utenti e dobbiamo raggruppare il campo nome degli utenti e calcolare il numero di utenti in ciascun gruppo. Di seguito è riportato un esempio di codice su come soddisfare questo requisito:

func Esegui(ctx context.Context, client *ent.Client) {
    nomi, err := client.User.
        Query().
        GroupBy(user.FieldName).
        Strings(ctx)
    if err != nil {
        log.Fatalf("Impossibile eseguire la query raggruppata: %v", err)
    }
    // Stampare il nome di ciascun gruppo
    for _, nome := range nomi {
        fmt.Println("Nome gruppo:", nome)
    }
}

Nel codice sopra, utilizziamo il metodo GroupBy del costruttore di query per specificare quale campo vogliamo raggruppare.

3.2. Raggruppamento e aggregazione di più campi

A volte, desideriamo raggruppare i dati basati su più campi e eseguire funzioni di aggregazione su ciascun gruppo. Di seguito è riportato un esempio su come soddisfare tale requisito:

Il codice seguente illustra come raggruppare i dati nella tabella degli utenti in base ai campi name e age e calcolare l'età totale e il numero di utenti in ciascun gruppo.

func Esegui(ctx context.Context, client *ent.Client) {
    var v []struct {
        Name  string `json:"name"`
        Age   int    `json:"age"`
        Sum   int    `json:"sum"`
        Count int    `json:"count"`
    }
    err := client.User.Query().
        GroupBy(user.FieldName, user.FieldAge).
        Aggregate(ent.Count(), ent.Sum(user.FieldAge)).
        Scan(ctx, &v)
    if err != nil {
        log.Fatalf("Impossibile eseguire la query di raggruppamento e aggregazione su più campi: %v", err)
    }
    // Output delle informazioni dettagliate per ciascun gruppo
    for _, group := range v {
        fmt.Printf("Nome: %s Età: %d Somma: %d Conteggio: %d\n", group.Name, group.Age, group.Sum, group.Count)
    }
}

In questo esempio, non solo raggruppiamo i dati basandoci sui campi name e age nella tabella degli utenti, ma utilizziamo anche le funzioni di aggregazione Count e Sum per calcolare il numero totale di record e l'età totale in ciascun gruppo.

4. Combinare Having con Group By

La clausola Having filtra i risultati aggregati ottenuti dopo l'operazione Group By. L'esempio seguente mostra come selezionare solo gli utenti con l'età massima in ciascun ruolo:

func Esegui(ctx context.Context, client *ent.Client) {
    var users []struct {
        Id      Int
        Age     Int
        Role    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, &users)
    if err != nil {
        log.Fatalf("Impossibile eseguire la query di combinazione di Having con Group By: %v", err)
    }
    // Output delle informazioni dell'utente che soddisfano la condizione di Having
    for _, user := range users {
        fmt.Printf("ID: %d Età: %d Ruolo: %s\n", user.Id, user.Age, user.Role)
    }
}

Il codice sopra genererà una query SQL equivalente per selezionare gli utenti con l'età massima in ciascun ruolo.