共用方式為


在 Data API 建構器中使用 GraphQL 彙整資料

Data API 建構器(DAB)支援 SQL 家族資料庫的 GraphQL 聚合與分組,以及 Azure Synapse Analytics(專用 SQL 池)。 聚合功能讓你能在不寫自訂 API 程式碼的情況下,彙整數值欄位並分組結果。 Azure Cosmos DB for NoSQL、PostgreSQL 或 MySQL 不支援聚合功能和groupBy

先決條件

  • 支援資料庫:
    • SQL Server 2016 或更新版本
    • Azure SQL Database
    • Azure SQL 受控執行個體
    • Microsoft Fabric SQL
    • Azure Synapse Analytics (僅限專用 SQL 池)
  • 數據 API 產生器 CLI。 安裝 CLI
  • 一個包含你實體的 DAB 設定檔,透過 GraphQL 公開。
  • 一個 GraphQL 客戶端(例如 Banana Cake Pop 或 GraphQL Playground)來執行查詢。

支援的資料庫

資料庫 聚合支援
SQL Server / Azure SQL / Microsoft Fabric SQL ✅ 是
Azure Synapse (Dedicated SQL pool) ✅ 是
Azure Synapse (Serverless SQL pool) ❌ 否
PostgreSQL ❌ 否
MySQL ❌ 否
Azure Cosmos DB for NoSQL ❌ 否

聚合函數

DAB 支援以下聚合函式:

功能 適用對象 Description
sum 僅允許數值欄位 所有數值的總和
average 僅限數值欄位 所有值的平均值
min 僅限數值欄位 最小值
max 僅允許數值的字段 最大值
count 任何領域 非空值的計數

Constraints

  • sum average min max、且僅適用於數值資料型別(整數、十進位、浮點數等)。
  • count 適用於任何資料型態,包括字串和日期。
  • 如果資料表沒有數字欄位,DAB 不會為該實體產生聚合節點。 你仍然可以在非數值欄位使用 count

可選修飾符

修飾語 目標 Example
distinct: true 只計算唯一值 計算不同的客戶
having: { ... } 聚合後的濾波器群組 顯示總和小於> 1000的群組

執行 DAB 執行環境

用你的設定檔啟動 DAB,這樣 GraphQL 端點就能使用。

dab start

查詢彙整結果

本節將完整示範資料表架構、GraphQL 查詢、產生的 SQL 與 JSON 回應。

資料表結構描述

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

GraphQL 查詢

使用 GraphQL 來分組列並回傳數值欄位的彙總值。

{
  books(
    groupBy: { fields: ["year"] }
  ) {
    items {
      year
    }
    aggregates {
      pages {
        sum
        average
        min
        max
      }
    }
  }
}
  • groupBy.fields 依指定欄位將資料列分組。
  • aggregates 揭示了數值欄位的聚合函數(例如, pages)。
  • GraphQL 架構僅對支援這些集合欄位開放;在客戶端使用結構內省來確認可用的聚合欄位和函式。

產生的 SQL

DAB 將 GraphQL 查詢轉換為 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

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 }
        ]
      }
    }
  }
}

itemsaggregates陣列依索引對齊——第一個aggregates.pages元素對應於 中的items第一個群組。

不帶分組的彙總

當你省略 groupBy時,計算所有列的聚合值。

GraphQL 查詢

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

產生的 SQL

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

JSON 回應

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

若沒有 groupBy,回應會回傳單一物件(非陣列),因為所有列合併為一個結果。

以一個或多個域分組

將資料列分組為一欄或多欄,並回傳每組的彙總結果。

資料表結構描述

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

GraphQL 查詢

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

產生的 SQL

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

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 }
        ]
      }
    }
  }
}

回應會回傳 items 和聚合值的陣列,並以相同的順序排列,讓你能將群組與其聚合值對齊。

必須篩選彙總結果

聚合後使用 having 來篩選群組。 這相當於 SQL 的 HAVING 子句。

資料表結構描述

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

GraphQL 查詢

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

產生的 SQL

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

JSON 回應

僅回傳總和超過10000的類別:

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

擁有運算元

Operator 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 }

備註

每個 having 篩選器獨立應用於其聚合函數。 你無法在單一 GraphQL 查詢中建立像是「sum > 1000 或 count < 10」這種跨聚合條件。

聚合中的獨特性

計算唯一值使用 distinct: true

資料表結構描述

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

GraphQL 查詢

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

產生的 SQL

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

JSON 回應

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

第一個 count (含 distinct: true)會為每位顧客返回獨一無二的產品。 第二個 count 會回傳總訂單。

備註

當在同一欄位請求多個聚合時,DAB 會依請求順序回傳。 使用別名(例如) uniqueProducts: count(distinct: true)來讓回應自我記錄。

將濾波器與聚合結合

在分組之前先應用 filter 於列,在聚合之後應用 having 於群組。 了解操作順序至關重要:

  1. 篩選 器(SQL WHERE)會在分組前移除列
  2. Group by 會將資料列分成群組
  3. Aggregate 計算每組的總和/平均/最小/最大/數量
  4. 有這個 設定會移除不符合條件的群組

GraphQL 查詢

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

產生的 SQL

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

小提示

在彙總之前,使用 filter 排除列。 聚合後使用 having 來篩選群組。

使用別名與聚合

使用 GraphQL 別名建立有意義的欄位名稱。

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

模式檢視

利用內省(introspection)來查看實體可用的聚合。

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

數值欄位會顯示 sumaverageminmaxcount。 非數值欄位則 count暴露 。

技巧與限制

  • 聚合和 groupBy 僅應用於 SQL Server、Azure SQL、Microsoft Fabric SQL 與 Azure Synapse 專用的 SQL 池。
  • 聚合體在數值欄位上運行; count 適用於任何領域。 沒有數值欄位的表格只會暴露 count
  • 群組化適用於同一實體上的欄位(不包含跨實體 groupBy)。
  • 大規模聚合的成本可能較高;在進行分組之前,請先為 GroupBy 欄位建立索引並篩選資料列。
  • 在常用 groupBy 欄位建立索引以提升查詢效能。

故障排除

錯誤:欄位不支援聚合

原因:在非數值欄位上使用 sumaverageminmax 、 。

解決方案:

  • 使用 schema 內省來驗證欄位類型。
  • 使用 count 於非數值欄位。
  • 如果使用自訂欄位名稱,請檢查欄位映射。

錯誤:找不到聚合節點

原因:該實體沒有數字欄位。

解決方案:

  • 請確認資料表結構至少有一個數字欄位。
  • 如果需要,可以在非數值欄位使用 count 聚合。

慢速彙總查詢

原因:資料表大且沒有適當的索引。

解決方案:

  • groupBy 欄位上建立索引。
  • 在合併前用 filter 來限制列數。
  • having 來減少退回的群組數量。

後續步驟