1. Pengenalan Analisis Agregat

Operasi agregasi merupakan konsep yang sangat penting dalam kueri basis data. Biasanya digunakan untuk analisis statistik seperti penjumlahan, penghitungan, rata-rata, nilai maksimum, dan minimum. Operasi-operasi ini membantu pengguna mengekstrak informasi yang bermakna dari sejumlah besar data, memberikan dukungan untuk analisis data dan pengambilan keputusan. Fungsi-fungsi yang diimplementasikan dalam basis data untuk agregasi biasanya disebut sebagai fungsi agregat.

2. Operasi Agregat Dasar

2.1 Konsep Fungsi Agregat

Fungsi agregat adalah fungsi yang digunakan dalam bahasa kueri basis data untuk melakukan serangkaian perhitungan dan mengembalikan satu nilai. Dalam SQL dan bahasa kueri serupa, fungsi agregat dapat beroperasi pada kolom data dan mengembalikan satu nilai, seperti penjumlahan (SUM), rata-rata (AVG), penghitungan (COUNT), maksimum (MAX), dan minimum (MIN). Ketika kita perlu melakukan analisis statistik data, fungsi agregat adalah alat penting untuk memproses himpunan data dan mengekstrak data statistik.

2.2 Agregasi Single-Field

Dalam aplikasi praktis, analisis agregasi single-field adalah kebutuhan yang sangat umum, sering digunakan untuk mendapatkan hasil statistik seperti jumlah, rata-rata, nilai maksimum, dan minimum dari suatu kolom tertentu. Misalkan kita memiliki tabel informasi pembayaran, dan ingin menghitung total jumlah yang dibayarkan oleh pengguna. Dengan menggunakan framework ent, kita dapat membuat kueri dari entitas dan menerapkan fungsi agregat sebagai berikut:

func Do(ctx context.Context, client *ent.Client) {
    // Menghitung jumlah dari kolom Amount untuk entitas Pembayaran
    sum, err := client.Payment.Query().
        Aggregate(
            ent.Sum(payment.Amount),
        ).
        Int(ctx)
    if err != nil {
        log.Fatalf("Gagal mendapatkan hasil jumlah: %v", err)
    }
    log.Printf("Total jumlah pembayaran: %d", sum)
}

Pada potongan kode di atas, kita memulai sebuah kueri untuk entitas pembayaran menggunakan client.Payment.Query(), kemudian menggunakan metode Aggregate() untuk memanggil fungsi ent.Sum dengan payment.Amount sebagai parameter untuk menghitung total jumlah pembayaran. Kita menggunakan .Int(ctx) untuk mengonversi hasil agregat menjadi bilangan bulat dan mencatatnya.

2.3 Agregasi Multi-Field

Dalam banyak kasus, kita perlu melakukan operasi agregasi pada beberapa kolom daripada hanya satu. Pada bagian ini, kita akan mendemonstrasikan bagaimana mencapai agregasi multi-field melalui sebuah contoh.

Pada contoh ini, kita akan menjumlahkan, mencari nilai minimum, mencari nilai maksimum, dan menghitung kolom Umur dalam tabel hewan peliharaan.

func Do(ctx context.Context, client *ent.Client) {
    var v []struct {
        Jumlah, Minimal, Maksimal, JumlahData int
    }
    err := client.Pet.Query().
        Aggregate(
            ent.Sum(pet.FieldAge), // Jumlah Umur
            ent.Min(pet.FieldAge), // Umur Minimal
            ent.Max(pet.FieldAge), // Umur Maksimal
            ent.Count(),           // Menghitung
        ).
        Scan(ctx, &v)
    if err != nil {
        log.Fatalf("Pencarian gagal: %v", err)
    }
    // Output semua hasil agregat
    for _, agg := range v {
        fmt.Printf("Jumlah: %d Minimal: %d Maksimal: %d Jumlah: %d\n", agg.Jumlah, agg.Minimal, agg.Maksimal, agg.JumlahData)
    }
}

Pada potongan kode di atas, kita menggunakan fungsi Aggregate untuk melakukan agregasi multi-field, dan menggunakan fungsi Scan untuk menyimpan hasil agregasi dalam slice v. Kemudian, kita melakukan iterasi melalui v untuk menampilkan semua hasil agregat.

3. Penerapan Agregasi Group By

3.1. Menggunakan Group By untuk Mengelompokkan Kolom

Dalam operasi basis data, Group By adalah metode umum untuk mengelompokkan data. Pada bagian ini, kita akan belajar bagaimana menggunakan Group By untuk mengelompokkan data dalam basis data.

Contoh tutorial, bagaimana mengelompokkan satu atau lebih kolom menggunakan Group By.

Misalkan kita memiliki tabel pengguna dan perlu mengelompokkan kolom nama pengguna serta menghitung jumlah pengguna dalam setiap kelompok. Berikut adalah contoh kode bagaimana mencapai kebutuhan ini:

func Do(ctx context.Context, client *ent.Client) {
    nama, err := client.User.
        Query().
        GroupBy(user.FieldName).
        Strings(ctx)
    if err != nil {
        log.Fatalf("Gagal menjalankan kueri yang dikelompokkan: %v", err)
    }
    // Output nama dari setiap kelompok
    for _, nam := range nama {
        fmt.Println("Nama kelompok:", nam)
    }
}

Pada kode di atas, kita menggunakan metode GroupBy dari pembangun kueri untuk menentukan kolom mana yang ingin kita kelompokkan.

3.2. Pengelompokan dan Penyatuan Beberapa Bidang

Terkadang, kita ingin mengelompokkan data berdasarkan beberapa bidang dan melakukan fungsi agregat pada setiap kelompok. Berikut adalah contoh bagaimana mencapai kebutuhan ini:

Berikut adalah contoh bagaimana mencapai kebutuhan ini:

Kode berikut menunjukkan bagaimana mengelompokkan data dalam tabel pengguna berdasarkan bidang name dan age, dan menghitung total usia dan jumlah pengguna di setiap kelompok.

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("Gagal menjalankan query pengelompokan dan agregasi beberapa bidang: %v", err)
    }
    // Output informasi detail untuk setiap kelompok
    for _, group := range v {
        fmt.Printf("Nama: %s Usia: %d Total: %d Jumlah: %d\n", group.Name, group.Age, group.Sum, group.Count)
    }
}

Pada contoh ini, kita tidak hanya mengelompokkan data berdasarkan bidang name dan age dalam tabel pengguna, tetapi juga menggunakan fungsi agregat Count dan Sum untuk menghitung total jumlah catatan dan total usia di setiap kelompok.

4. Menggabungkan Having dengan Group By

Klausa Having menyaring hasil agregat yang diperoleh setelah operasi Group By. Contoh berikut menunjukkan bagaimana memilih hanya pengguna dengan usia maksimum di setiap peran:

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("Gagal menjalankan query Having yang digabungkan dengan Group By: %v", err)
    }
    // Output informasi pengguna yang memenuhi kondisi Having
    for _, user := range users {
        fmt.Printf("ID: %d Usia: %d Peran: %s\n", user.Id, user.Age, user.Role)
    }
}

Kode di atas akan menghasilkan query SQL setara untuk memilih pengguna dengan usia maksimum di setiap peran.