1. Wprowadzenie do Analizy Agregatów
Operacja agregacji to bardzo ważne pojęcie w zapytaniach do bazy danych. Zazwyczaj używana jest do analizy statystycznej, taka jak sumowanie, zliczanie, obliczanie średniej, znajdowanie wartości maksymalnej i minimalnej. Te operacje pomagają użytkownikom wyciągać znaczące informacje z dużej ilości danych, zapewniając wsparcie dla analizy danych i podejmowania decyzji. Funkcje realizujące agregację w bazie danych zwykle nazywane są funkcjami agregatowymi.
2. Podstawowe Operacje Agregacji
2.1 Pojęcie Funkcji Agregatowych
Funkcje agregatowe są funkcjami używanymi w językach zapytań do bazy danych do przeprowadzenia serii obliczeń i zwrócenia pojedynczej wartości. W języku SQL i podobnych językach zapytań, funkcje agregatowe mogą działać na kolumnie danych i zwrócić pojedynczą wartość, taką jak suma (SUM), średnia (AVG), zliczanie (COUNT), maksymalna (MAX) i minimalna (MIN). Gdy potrzebujemy przeprowadzić analizę statystyczną danych, funkcje agregatowe są ważnym narzędziem do przetwarzania zestawów danych i wyodrębniania danych statystycznych.
2.2 Agregacja Pojedynczego Pola
W praktyce, analiza agregacji jednego pola jest bardzo częstym wymaganiem, często używanym do uzyskania wyników statystycznych, takich jak suma, średnia, maksimum i minimum danej kolumny. Załóżmy, że mamy tabelę informacji o płatnościach i chcemy obliczyć całkowitą kwotę zapłaconą przez użytkowników. Korzystając z frameworka ent
, możemy skonstruować zapytanie z encji i zastosować funkcje agregatowe w sposób następujący:
func Do(ctx context.Context, client *ent.Client) {
// Oblicz sumę pola Amount dla encji Payment
suma, err := client.Payment.Query().
Aggregate(
ent.Sum(payment.Amount),
).
Int(ctx)
if err != nil {
log.Fatalf("Nie udało się uzyskać sumy: %v", err)
}
log.Printf("Całkowita kwota płatności: %d", suma)
}
W powyższym fragmencie kodu inicjujemy zapytanie dla encji płatności używając client.Payment.Query()
, następnie używamy metody Aggregate()
do wywołania funkcji ent.Sum
z payment.Amount
jako parametrem do obliczenia całkowitej kwoty płatności. Używamy .Int(ctx)
do konwersji wyniku agregacji na liczbę całkowitą i zalogowania tego.
2.3 Agregacja Wielu Pól
W wielu przypadkach potrzebujemy przeprowadzić operacje agregacji na wielu polach, a nie tylko na jednym. W tej sekcji, pokażemy, jak osiągnąć agregację wielu pól na przykładzie.
W tym przykładzie, będziemy sumować, znaleźć minimum, maksimum i policzyć pole Age
w tabeli pet.
func Do(ctx context.Context, client *ent.Client) {
var v []struct {
Suma, Min, Max, Count int
}
err := client.Pet.Query().
Aggregate(
ent.Sum(pet.FieldAge), // Suma wieku
ent.Min(pet.FieldAge), // Minimum wiek
ent.Max(pet.FieldAge), // Maksimum wiek
ent.Count(), // Licznik
).
Scan(ctx, &v)
if err != nil {
log.Fatalf("Zapytanie nie powiodło się: %v", err)
}
// Wypisanie wszystkich wyników agregacji
for _, agg := range v {
fmt.Printf("Suma: %d Min: %d Max: %d Liczba: %d\n", agg.Suma, agg.Min, agg.Max, agg.Count)
}
}
W powyższym kodzie, używamy funkcji Aggregate
do przeprowadzenia operacji agregacji wielu pól i używamy funkcji Scan
do przechowywania wyników agregacji w slicu v
. Następnie, iterujemy przez v
, aby wypisać wszystkie wyniki agregacji.
3. Zastosowanie Agregacji Group By
3.1. Korzystanie z Group By do Grupowania Pól
W operacjach bazodanowych, Group By
jest powszechną metodą grupowania danych. W tej sekcji, dowiemy się, jak korzystać z Group By
do grupowania danych w bazie danych.
Przykładowy samouczek, jak grupować jedno lub więcej pól za pomocą Group By.
Załóżmy, że mamy tabelę użytkowników i potrzebujemy zgrupować pole name
użytkowników oraz obliczyć liczbę użytkowników w każdej grupie. Poniżej jest przykładowy kod, jak osiągnąć to wymaganie:
func Do(ctx context.Context, client *ent.Client) {
nazwy, err := client.User.
Query().
GroupBy(user.FieldName).
Strings(ctx)
if err != nil {
log.Fatalf("Nie udało się wykonać zapytania grupującego: %v", err)
}
// Wypisz nazwę każdej grupy
for _, name := range nazwy {
fmt.Println("Nazwa grupy:", name)
}
}
W powyższym kodzie, używamy metody GroupBy
kreatora zapytań do określenia, według którego pola chcemy grupować.
3.2. Grupowanie i agregacja wielu pól
Czasem chcemy pogrupować dane na podstawie wielu pól i wykonać funkcje agregujące w każdej grupie. Poniżej znajduje się przykład, jak to osiągnąć:
Poniższy kod przedstawia, jak pogrupować dane w tabeli użytkowników na podstawie pól name
i age
, oraz obliczyć sumę wieku i liczbę użytkowników w każdej grupie.
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("Nie udało się wykonać zapytania grupowania i agregacji wielu pól: %v", err)
}
// Wyświetlenie szczegółowych informacji dla każdej grupy
for _, group := range v {
fmt.Printf("Name: %s Age: %d Sum: %d Count: %d\n", group.Name, group.Age, group.Sum, group.Count)
}
}
W tym przykładzie grupujemy dane nie tylko na podstawie pól name
i age
w tabeli użytkowników, ale także używamy funkcji agregujących Count
i Sum
do obliczenia całkowitej liczby rekordów i całkowitego wieku w każdej grupie.
4. Łączenie Having z Group By
Klauzula Having
filtrowe wyniki agregacji uzyskane po operacji Group By
. Poniższy przykład pokazuje, jak wybrać tylko użytkowników z maksymalnym wiekiem w każdej roli:
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("Nie udało się wykonać zapytania Having połączonego z Group By: %v", err)
}
// Wyświetlenie informacji o użytkowniku, który spełnia warunek Having
for _, user := range users {
fmt.Printf("ID: %d Age: %d Role: %s\n", user.Id, user.Age, user.Role)
}
}
Powyższy kod wygeneruje równoważne zapytanie SQL do wybrania użytkowników z maksymalnym wiekiem w każdej roli.