Bagikan melalui


Mengagregasikan data dengan GraphQL menggunakan pembangun API Data

Penyusun API Data (DAB) mendukung agregasi dan pengelompokan GraphQL untuk database keluarga SQL dan Azure Synapse Analytics (kumpulan SQL Khusus). Agregasi memungkinkan Anda meringkas bidang numerik dan hasil grup tanpa menulis kode API kustom. Agregasi dan groupBy tidak tersedia untuk Azure Cosmos DB untuk NoSQL, PostgreSQL, atau MySQL.

Prasyarat

  • Database yang didukung:
    • SQL Server 2016 atau yang lebih baru
    • Azure SQL Database
    • Azure SQL Managed Instance
    • Microsoft Fabric SQL
    • Azure Synapse Analytics (kumpulan SQL Terdedikasi)
  • CLI pembangun API Data. Menginstal CLI
  • File konfigurasi DAB dengan entitas Anda ditampilkan menggunakan GraphQL.
  • Klien GraphQL (misalnya, Banana Cake Pop atau GraphQL Playground) untuk menjalankan kueri.

Database yang didukung

Database Dukungan agregasi
SQL Server / Azure SQL / Microsoft Fabric SQL ✅ Ya
Azure Synapse (Kumpulan SQL Khusus) ✅ Ya
Azure Synapse (Kumpulan SQL Tanpa Server) ❌ Tidak
PostgreSQL ❌ Tidak
MySQL ❌ Tidak
Azure Cosmos DB for NoSQL ❌ Tidak

Fungsi agregat

DAB mendukung fungsi agregat berikut:

Function Berlaku pada Description
sum Bidang numerik saja Jumlah total nilai
average Bidang numerik saja Rata-rata semua nilai
min Bidang numerik saja Nilai minimum
max Bidang numerik saja Nilai maksimal
count Bidang apa pun Jumlah nilai non-null

Constraints

  • sum, , average, mindan max hanya bekerja pada jenis data numerik (int, desimal, float, dll.).
  • count bekerja pada jenis data apa pun, termasuk string dan tanggal.
  • Jika tabel tidak memiliki kolom numerik, DAB tidak menghasilkan simpul agregasi untuk entitas tersebut. Anda masih dapat menggunakan count pada bidang non-numerik.

Pengubah opsional

Pengubah Tujuan Example
distinct: true Hitung nilai unik saja Menghitung pelanggan yang berbeda
having: { ... } Memilah grup setelah agregasi Perlihatkan grup dengan jumlah > 1000

Menjalankan runtime DAB

Mulai DAB dengan file konfigurasi Anda agar endpoint GraphQL dapat diakses.

dab start

Hasil agregat kueri

Bagian ini menjelaskan contoh lengkap yang memperlihatkan skema tabel, kueri GraphQL, respons SQL yang dihasilkan, dan JSON.

Skema tabel

CREATE TABLE books (
    id INT PRIMARY KEY,
    title NVARCHAR(200),
    year INT,
    pages INT
);

Kueri GraphQL

Gunakan GraphQL untuk mengelompokkan baris dan mengembalikan nilai agregat untuk bidang numerik.

{
  books(
    groupBy: { fields: ["year"] }
  ) {
    items {
      year
    }
    aggregates {
      pages {
        sum
        average
        min
        max
      }
    }
  }
}
  • groupBy.fields mengelompokkan baris menurut kolom yang ditentukan.
  • aggregates mengekspos fungsi agregat untuk bidang numerik (misalnya, pages).
  • Skema GraphQL hanya mengekspos agregat untuk bidang yang mendukungnya; gunakan introspeksi skema di klien Anda untuk mengonfirmasi bidang dan fungsi agregat yang tersedia.

SQL yang dihasilkan

DAB menerjemahkan kueri GraphQL ke dalam T-SQL:

SELECT 
    [year],
    SUM([pages]) AS [sum],
    AVG([pages]) AS [average],
    MIN([pages]) AS [min],
    MAX([pages]) AS [max]
FROM [dbo].[books]
GROUP BY [year]
FOR JSON PATH, INCLUDE_NULL_VALUES

Tanggapan JSON

{
  "data": {
    "books": {
      "items": [
        { "year": 2023 },
        { "year": 2024 }
      ],
      "aggregates": {
        "pages": [
          { "sum": 3200, "average": 320, "min": 120, "max": 450 },
          { "sum": 4500, "average": 300, "min": 140, "max": 510 }
        ]
      }
    }
  }
}

Array items dan aggregates disejajarkan menurut indeks—elemen pertama di aggregates.pages sesuai dengan grup pertama di items.

Agregat tanpa pengelompokan

Hitung agregat di semua baris saat Anda menghilangkan groupBy.

Kueri GraphQL

{
  books {
    aggregates {
      pages {
        sum
        average
        min
        max
        count
      }
      id {
        count
      }
    }
  }
}

SQL yang dihasilkan

SELECT
    SUM([pages]) AS [sum],
    AVG([pages]) AS [average],
    MIN([pages]) AS [min],
    MAX([pages]) AS [max],
    COUNT([pages]) AS [count],
    COUNT([id]) AS [count]
FROM [dbo].[books]
FOR JSON PATH, INCLUDE_NULL_VALUES

Tanggapan JSON

{
  "data": {
    "books": {
      "aggregates": {
        "pages": {
          "sum": 15420,
          "average": 308,
          "min": 120,
          "max": 850,
          "count": 50
        },
        "id": {
          "count": 50
        }
      }
    }
  }
}

Tanpa groupBy, respons mengembalikan satu objek (bukan array) karena semua baris diciutkan menjadi satu hasil.

Mengelompokkan menurut satu atau beberapa bidang

Kelompokkan baris menurut satu atau beberapa kolom dan kembalikan agregat per grup.

Skema tabel

CREATE TABLE sales (
    id INT PRIMARY KEY,
    year INT,
    category NVARCHAR(50),
    revenue DECIMAL(10,2),
    quantity INT
);

Kueri GraphQL

{
  sales(
    groupBy: { fields: ["year", "category"] }
  ) {
    items {
      year
      category
    }
    aggregates {
      revenue {
        sum
        average
      }
      quantity {
        sum
      }
    }
  }
}

SQL yang dihasilkan

SELECT
    [year],
    [category],
    SUM([revenue]) AS [sum],
    AVG([revenue]) AS [average],
    SUM([quantity]) AS [sum]
FROM [dbo].[sales]
GROUP BY [year], [category]
FOR JSON PATH, INCLUDE_NULL_VALUES

Tanggapan JSON

{
  "data": {
    "sales": {
      "items": [
        { "year": 2023, "category": "Books" },
        { "year": 2023, "category": "Electronics" },
        { "year": 2024, "category": "Books" }
      ],
      "aggregates": {
        "revenue": [
          { "sum": 45000.00, "average": 150.00 },
          { "sum": 120000.00, "average": 600.00 },
          { "sum": 52000.00, "average": 173.33 }
        ],
        "quantity": [
          { "sum": 300 },
          { "sum": 200 },
          { "sum": 300 }
        ]
      }
    }
  }
}

Respons mengembalikan array untuk items dan mengagregasi dalam urutan yang sama sehingga Anda dapat meratakan grup dengan nilai agregatnya.

Penggunaan "HAVING" untuk memfilter hasil agregat

Gunakan having untuk memfilter grup setelah agregasi. Ini setara dengan klausul SQL HAVING .

Skema tabel

CREATE TABLE products (
    id INT PRIMARY KEY,
    category NVARCHAR(50),
    price DECIMAL(10,2)
);

Kueri GraphQL

{
  products(
    groupBy: { fields: ["category"] }
  ) {
    items { category }
    aggregates {
      price {
        sum(having: { gt: 10000 })
        average
      }
    }
  }
}

SQL yang dihasilkan

SELECT
    [category],
    SUM([price]) AS [sum],
    AVG([price]) AS [average]
FROM [dbo].[products]
GROUP BY [category]
HAVING SUM([price]) > 10000
FOR JSON PATH, INCLUDE_NULL_VALUES

Tanggapan JSON

Hanya kategori di mana jumlah melebihi 10000 dikembalikan:

{
  "data": {
    "products": {
      "items": [
        { "category": "Electronics" },
        { "category": "Furniture" }
      ],
      "aggregates": {
        "price": [
          { "sum": 15000.00, "average": 300.00 },
          { "sum": 12000.00, "average": 400.00 }
        ]
      }
    }
  }
}

Operator HAVING

Operator Setara dengan SQL Example
eq = having: { eq: 100 }
neq <> having: { neq: 0 }
gt > having: { gt: 1000 }
gte >= having: { gte: 500 }
lt < having: { lt: 100 }
lte <= having: { lte: 50 }

Nota

Setiap having filter berlaku secara independen pada fungsi agregatnya. Anda tidak dapat membuat kondisi agregat silang seperti "jumlah > 1000 ATAU hitungan < 10" dalam satu kueri GraphQL.

BERBEDA dalam agregasi

Hitung nilai unik dengan distinct: true.

Skema tabel

CREATE TABLE orders (
    id INT PRIMARY KEY,
    customer_id INT,
    product_id INT
);

Kueri GraphQL

{
  orders(
    groupBy: { fields: ["customer_id"] }
  ) {
    items { customer_id }
    aggregates {
      product_id {
        count(distinct: true)
        count
      }
    }
  }
}

SQL yang dihasilkan

SELECT
    [customer_id],
    COUNT(DISTINCT [product_id]) AS [count],
    COUNT([product_id]) AS [count]
FROM [dbo].[orders]
GROUP BY [customer_id]
FOR JSON PATH, INCLUDE_NULL_VALUES

Tanggapan JSON

{
  "data": {
    "orders": {
      "items": [
        { "customer_id": 101 },
        { "customer_id": 102 }
      ],
      "aggregates": {
        "product_id": [
          { "count": 5 },
          { "count": 3 }
        ]
      }
    }
  }
}

Yang pertama count (dengan distinct: true) menghasilkan produk unik per pelanggan. Yang kedua count mengembalikan total pesanan.

Nota

Saat meminta beberapa agregat pada bidang yang sama, DAB mengembalikannya dalam urutan yang diminta. Gunakan alias (misalnya, uniqueProducts: count(distinct: true)) untuk membuat respons dokumentasi mandiri.

Padu padankan filter dengan agregasi

Terapkan filter ke baris sebelum pengelompokan, dan having ke grup setelah agregasi. Memahami urutan operasi sangat penting:

  1. Filter (SQL WHERE) menghapus baris sebelum pengelompokan
  2. Group by mengumpulkan baris yang tersisa menjadi kelompok-kelompok
  3. Agregat menghitung total/rata-rata/min/maks/jumlah data per grup
  4. Pada HAVING grup yang tidak cocok dengan kondisi dihapus

Kueri GraphQL

{
  sales(
    filter: { year: { gte: 2023 } }
    groupBy: { fields: ["region"] }
  ) {
    items { region }
    aggregates {
      revenue { sum average }
    }
  }
}

SQL yang dihasilkan

SELECT
    [region],
    SUM([revenue]) AS [sum],
    AVG([revenue]) AS [average]
FROM [dbo].[sales]
WHERE [year] >= 2023
GROUP BY [region]
FOR JSON PATH, INCLUDE_NULL_VALUES

Petunjuk / Saran

Gunakan filter untuk mengecualikan baris sebelum agregasi. Gunakan having untuk memfilter grup setelah agregasi.

Menggunakan alias dengan agregasi

Buat nama bidang yang bermakna menggunakan alias GraphQL.

{
  products(
    groupBy: { fields: ["category"] }
  ) {
    items { category }
    aggregates {
      price {
        totalRevenue: sum
        avgPrice: average
        cheapest: min
        mostExpensive: max
        productCount: count
      }
    }
  }
}

Introspeksi skema

Gunakan introspeksi untuk melihat agregat mana yang tersedia untuk entitas.

{
  __type(name: "BooksAggregates") {
    fields {
      name
      type { name }
    }
  }
}

Bidang numerik mengekspos sum, , average, minmax, dan count. Bidang non-numerik mengekspos count.

Tips dan batasan

  • Agregasi dan groupBy berlaku untuk kumpulan SQL Server, Azure SQL, Microsoft Fabric SQL, dan Azure Synapse Dedicated SQL saja.
  • Agregat berjalan pada bidang numerik; count bekerja di bidang apa pun. Tabel tanpa kolom numerik hanya mengekspos count.
  • Pengelompokan berlaku untuk bidang pada entitas yang sama (tidak ada groupBy lintas entitas).
  • Agregasi yang besar bisa mahal; lakukan pengindeksan pada kolom-kolom yang digunakan dalam perintah groupBy dan lakukan penyaringan baris sebelum mengelompokkan jika memungkinkan.
  • Buat indeks pada kolom yang sering digunakan groupBy untuk meningkatkan performa kueri.

Troubleshooting

Kesalahan: Bidang tidak mendukung agregasi

Penyebab: Menggunakan sum, average, min, atau max pada bidang non-numerik.

Solusi:

  • Gunakan introspeksi skema untuk memverifikasi jenis bidang.
  • Gunakan count untuk bidang non-numerik.
  • Periksa pemetaan bidang jika menggunakan nama bidang kustom.

Kesalahan: Simpul agregasi tidak ditemukan

Penyebab: Entitas tidak memiliki kolom numerik.

Solusi:

  • Verifikasi skema tabel memiliki setidaknya satu kolom numerik.
  • Gunakan count agregat pada bidang non-numerik jika diperlukan.

Kueri agregasi lambat

Penyebab: Tabel besar tanpa indeks yang tepat.

Solusi:

  • Membuat indeks pada groupBy kolom.
  • Gunakan filter untuk membatasi baris sebelum agregasi.
  • Gunakan having untuk mengurangi jumlah grup yang dikembalikan.

Langkah selanjutnya