1. Einführung in die Aggregatanalyse

Die Aggregatoperation ist ein sehr wichtiges Konzept in Datenbankabfragen. Sie wird in der Regel für statistische Analysen wie Summierung, Zählen, Durchschnitt, Maximal- und Minimalwerte verwendet. Diese Operationen helfen Benutzern, sinnvolle Informationen aus einer großen Menge von Daten zu extrahieren, indem sie Unterstützung für die Datenanalyse und Entscheidungsfindung bieten. Die in der Datenbank implementierten Funktionen für die Aggregation werden in der Regel als Aggregatfunktionen bezeichnet.

2. Grundlegende Aggregatoperationen

2.1 Konzept der Aggregatfunktionen

Aggregatfunktionen sind Funktionen, die in Datenbankabfragesprachen verwendet werden, um eine Reihe von Berechnungen durchzuführen und einen einzelnen Wert zurückzugeben. In SQL und ähnlichen Abfragesprachen können Aggregatfunktionen auf eine Datenspalte angewendet werden und einen einzelnen Wert wie Summe (SUM), Durchschnitt (AVG), Anzahl (COUNT), Maximum (MAX) und Minimum (MIN) zurückgeben. Bei der Durchführung von statistischen Datenanalysen sind Aggregatfunktionen wichtige Werkzeuge zur Verarbeitung von Datensätzen und Extraktion statistischer Daten.

2.2 Aggregation mit einer Feld

In der Praxis ist die Analyse der Aggregation mit einem Feld eine sehr häufige Anforderung, die oft zur Erlangung statistischer Ergebnisse wie Summe, Durchschnitt, Maximal- und Minimalwerte einer bestimmten Spalte verwendet wird. Angenommen, wir haben eine Tabelle mit Zahlungsinformationen und möchten den Gesamtbetrag berechnen, den Benutzer gezahlt haben. Mit dem ent-Framework können wir eine Abfrage aus der Entität erstellen und Aggregatfunktionen wie folgt anwenden:    

func Do(ctx context.Context, client *ent.Client) {
    // Berechnen der Summe des Betragsfelds für die Zahlungsentität
    sum, err := client.Payment.Query().
        Aggregate(
            ent.Sum(payment.Amount),
        ).
        Int(ctx)
    if err != nil {
        log.Fatalf("Fehler beim Abrufen der Summe: %v", err)
    }
    log.Printf("Gesamtbetrag der Zahlungen: %d", sum)
}

In obigem Codeausschnitt initiieren wir eine Abfrage für die Zahlungsentität mit client.Payment.Query(), verwenden dann die Methode Aggregate(), um die Funktion ent.Sum mit payment.Amount als Parameter aufzurufen, um den Gesamtbetrag der Zahlungen zu berechnen. Wir verwenden .Int(ctx), um das aggregierte Ergebnis in eine ganze Zahl umzuwandeln und protokollieren es.

2.3 Aggregation mit mehreren Feldern

In vielen Fällen müssen wir Aggregationsoperationen auf mehreren Feldern anstelle von nur einem durchführen. In diesem Abschnitt werden wir anhand eines Beispiels zeigen, wie die Aggregation mit mehreren Feldern erreicht werden kann.

In diesem Beispiel summieren, finden das Minimum, das Maximum und zählen das Feld Age in der Haustiertabelle.

func Do(ctx context.Context, client *ent.Client) {
    var v []struct {
        Sum, Min, Max, Count int
    }
    err := client.Pet.Query().
        Aggregate(
            ent.Sum(pet.FieldAge), // Summe des Alters
            ent.Min(pet.FieldAge), // Mindestalter
            ent.Max(pet.FieldAge), // Höchstalter
            ent.Count(),           // Anzahl
        ).
        Scan(ctx, &v)
    if err != nil {
        log.Fatalf("Abfrage fehlgeschlagen: %v", err)
    }
    // Ausgabe aller aggregierten Ergebnisse
    for _, agg := range v {
        fmt.Printf("Summe: %d Min: %d Max: %d Anzahl: %d\n", agg.Sum, agg.Min, agg.Max, agg.Count)
    }
}

In obigem Code verwenden wir die Funktion Aggregate, um die Aggregation mit mehreren Feldern durchzuführen, und verwenden die Funktion Scan, um die Aggregationsergebnisse im Slice v zu speichern. Anschließend iterieren wir durch v, um alle aggregierten Ergebnisse auszugeben.

3. Anwendung der Gruppenaggregation

3.1. Verwendung von Group By zum Gruppieren von Feldern

In Datenbankoperationen ist "Group By" eine übliche Methode zum Gruppieren von Daten. In diesem Abschnitt lernen wir, wie "Group By" verwendet wird, um Daten in der Datenbank zu gruppieren.

Beispielanleitung: Wie man ein oder mehrere Felder mit Group By gruppieren kann.

Angenommen, wir haben eine Benutzertabelle und müssen das Feld name der Benutzer gruppieren und die Anzahl der Benutzer in jeder Gruppe berechnen. Unten ist ein Codebeispiel, das diese Anforderung erfüllt:

func Do(ctx context.Context, client *ent.Client) {
    names, err := client.User.
        Query().
        GroupBy(user.FieldName).
        Strings(ctx)
    if err != nil {
        log.Fatalf("Fehler beim Ausführen der gruppierten Abfrage: %v", err)
    }
    // Ausgabe des Namens jeder Gruppe
    for _, name := range names {
        fmt.Println("Gruppenname:", name)
    }
}

In obigem Code verwenden wir die Methode GroupBy des Abfrage-Builders, um zu spezifizieren, nach welchem Feld wir gruppieren möchten.

3.2. Gruppierung und Aggregation mehrerer Felder

Manchmal möchten wir Daten basierend auf mehreren Feldern gruppieren und für jede Gruppe Aggregatfunktionen durchführen. Nachfolgend ein Beispiel, wie diese Anforderung erfüllt werden kann:

Der folgende Code zeigt, wie Daten in der Benutzertabelle basierend auf den Feldern name und age gruppiert und die Gesamtsumme des Alters sowie die Anzahl der Benutzer in jeder Gruppe berechnet werden.

func Do(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("Fehler bei der Ausführung der Abfrage zur Gruppierung und Aggregation mehrerer Felder: %v", err)
    }
    // Ausgabe detaillierter Informationen für jede Gruppe
    for _, group := range v {
        fmt.Printf("Name: %s Alter: %d Summe: %d Anzahl: %d\n", group.Name, group.Age, group.Sum, group.Count)
    }
}

In diesem Beispiel gruppieren wir nicht nur die Daten basierend auf den Feldern name und age in der Benutzertabelle, sondern verwenden auch die Aggregatfunktionen Count und Sum, um die Gesamtanzahl an Datensätzen und das Gesamtalter in jeder Gruppe zu berechnen.

4. Kombination von Having mit Group By

Die Having-Klausel filtert die Aggregatergebnisse, die nach der Group By-Operation erhalten wurden. Das folgende Beispiel zeigt, wie nur die Benutzer mit dem höchsten Alter in jeder Rolle ausgewählt werden:

func Do(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("Fehler bei der Ausführung der Abfrage zur Kombination von Having mit Group By: %v", err)
    }
    // Ausgabe von Benutzerinformationen, die der Having-Bedingung entsprechen
    for _, user := range users {
        fmt.Printf("ID: %d Alter: %d Rolle: %s\n", user.Id, user.Age, user.Role)
    }
}

Der obige Code generiert eine äquivalente SQL-Abfrage, um die Benutzer mit dem höchsten Alter in jeder Rolle auszuwählen.