Data API 建置器支援對 SQL Server 產品系列和 Azure Synapse Analytics 專用 SQL 集區中的實體進行 GraphQL 彙總。 在集合查詢中使用該 groupBy 欄位來計算 sum、 avg、 min、 max和 count 值。
本文範例使用 SQL Server 與 GraphQL 實體,並取得讀取權限。 Data API 建構器會產生類似每個情境中陳述的 SQL 指令。 參數值會在執行時以查詢參數的形式出現。
sum這些 、 avg、 min和 max 函數適用於數值欄位。 這個 count 函數適用於任何領域。
Important
Azure Cosmos DB for NoSQL、PostgreSQL 或 MySQL 無法提供聚合功能。
聚合預設是啟用的。 若要將其關閉,請在設定檔中的 runtime.graphql 下,將 enable-aggregation 設為 false。 聚合查詢會回傳一頁群組,預設為 100 頁。 在集合查詢中使用 first 參數來改變最大值,例如 books(first: 500)。 預設值設為 runtime.pagination.default-page-size。
GraphQL 結構描述的新增內容
啟用聚合後,Data API 建構器會為每個支援的 GraphQL 集合新增聚合欄位和產生的型別。 精確產生的型別名稱是實體專屬的,並可透過 GraphQL 內省看到,但查詢語法在各實體間保持一致。
以下摘要顯示語法格式,並非完整的 GraphQL 查詢。
groupBy
回傳集合中已分組的資料列。 在彙總查詢中,選取此成員以取代 items。
<collection> { groupBy { ... } }
groupBy(fields: [...])
列出用於分組的實體欄位。 省略 fields 將所有列合併成一組。
<collection> { groupBy(fields: [<field>, ...]) { ... } }
fields
回傳彙總結果中每一列的群組欄位值。
groupBy(fields: [<field>]) { fields { <field> } }
aggregations
包含每個群組的彙總函式選擇。
groupBy { aggregations { <alias>: <function>(field: <field>) } }
sum、avg、min 和 max
彙總數值欄位。
aggregations { <alias>: sum(field: <numeric-field>) }
aggregations { <alias>: avg(field: <numeric-field>) }
aggregations { <alias>: min(field: <numeric-field>) }
aggregations { <alias>: max(field: <numeric-field>) }
count
計算欄位的數值。
aggregations { <alias>: count(field: <field>) }
field
識別要彙整的實體欄位。 欄位名稱是列舉值,不是字串。
<function>(field: <field>)
having
在 Data API 建構器計算彙總值後,篩選群組。
aggregations { <alias>: <function>(field: <field>, having: { <operator>: <value> }) }
distinct
與 count 搭配使用時,會計算不重複的值。
aggregations { <alias>: count(field: <field>, distinct: true) }
Data API 建構器會在這些成員背後產生實體專屬的 GraphQL 類型,包括群組列類型、群組欄位類型、聚合選擇類型、欄位列舉及 having 輸入類型。 你不需要在查詢中命名那些產生的類型。
Samples
以下範例展示了常見聚合模式的 SQL 表格、GraphQL 查詢、結果 SQL 及輸出。
彙總資料表中的所有資料列
當您想為整個實體建立一列摘要時,請使用此模式。
SQL 表格
CREATE TABLE dbo.Books (
id INT NOT NULL PRIMARY KEY,
title NVARCHAR(200) NOT NULL,
[year] INT NOT NULL,
pages INT NOT NULL
);
INSERT INTO dbo.Books (id, title, [year], pages) VALUES
(1, N'GraphQL Basics', 2023, 120),
(2, N'Advanced APIs', 2023, 450),
(3, N'Data Patterns', 2023, 390),
(4, N'Cloud APIs', 2024, 140),
(5, N'Runtime Internals', 2024, 510),
(6, N'Query Tuning', 2024, 250);
| id | title | 年 | pages |
|---|---|---|---|
| 1 | GraphQL 基礎 | 2023 | 120 |
| 2 | 進階 API | 2023 | 450 |
| 3 | 資料模式 | 2023 | 390 |
| 4 | 雲端 API | 2024 | 140 |
| 5 | 執行時內部結構 | 2024 | 510 |
| 6 | 查詢最佳化 | 2024 | 250 |
GraphQL 查詢
{
books {
groupBy {
aggregations {
totalPages: sum(field: pages)
averagePages: avg(field: pages)
shortestBook: min(field: pages)
longestBook: max(field: pages)
bookCount: count(field: id)
}
}
}
}
產生的 SQL
SELECT TOP 100
SUM([table0].[pages]) AS [totalPages],
AVG([table0].[pages]) AS [averagePages],
MIN([table0].[pages]) AS [shortestBook],
MAX([table0].[pages]) AS [longestBook],
COUNT([table0].[id]) AS [bookCount]
FROM [dbo].[Books] AS [table0]
WHERE 1 = 1
FOR JSON PATH, INCLUDE_NULL_VALUES;
結果輸出
{
"data": {
"books": {
"groupBy": [
{
"aggregations": {
"totalPages": 1860,
"averagePages": 310,
"shortestBook": 120,
"longestBook": 510,
"bookCount": 6
}
}
]
}
}
}
| 總頁數 | 平均頁數 | 最短的書 | 最長書籍 | 書籍數量 |
|---|---|---|---|---|
| 1860 | 310 | 120 | 510 | 6 |
依單一欄位將資料列分組
使用 groupBy(fields: [...]),讓每個欄位值各回傳一列彙總資料列。 欄位名稱是 GraphQL 的列舉值,而非字串。
SQL 表格
CREATE TABLE dbo.Books (
id INT NOT NULL PRIMARY KEY,
title NVARCHAR(200) NOT NULL,
[year] INT NOT NULL,
pages INT NOT NULL
);
INSERT INTO dbo.Books (id, title, [year], pages) VALUES
(1, N'GraphQL Basics', 2023, 120),
(2, N'Advanced APIs', 2023, 450),
(3, N'Data Patterns', 2023, 390),
(4, N'Cloud APIs', 2024, 140),
(5, N'Runtime Internals', 2024, 510),
(6, N'Query Tuning', 2024, 250);
| id | title | 年 | pages |
|---|---|---|---|
| 1 | GraphQL 基礎 | 2023 | 120 |
| 2 | 進階 API | 2023 | 450 |
| 3 | 資料模式 | 2023 | 390 |
| 4 | 雲端 API | 2024 | 140 |
| 5 | 執行時內部結構 | 2024 | 510 |
| 6 | 查詢最佳化 | 2024 | 250 |
GraphQL 查詢
{
books(orderBy: { year: ASC }) {
groupBy(fields: [year]) {
fields { year }
aggregations {
totalPages: sum(field: pages)
averagePages: avg(field: pages)
}
}
}
}
產生的 SQL
SELECT TOP 100
[table0].[year] AS [year],
SUM([table0].[pages]) AS [totalPages],
AVG([table0].[pages]) AS [averagePages]
FROM [dbo].[Books] AS [table0]
WHERE 1 = 1
GROUP BY [table0].[year]
ORDER BY [table0].[year] ASC
FOR JSON PATH, INCLUDE_NULL_VALUES;
結果輸出
{
"data": {
"books": {
"groupBy": [
{
"fields": {
"year": 2023
},
"aggregations": {
"totalPages": 960,
"averagePages": 320
}
},
{
"fields": {
"year": 2024
},
"aggregations": {
"totalPages": 900,
"averagePages": 300
}
}
]
}
}
}
| 年 | 總頁數 | 平均頁數 |
|---|---|---|
| 2023 | 960 | 320 |
| 2024 | 900 | 300 |
在檢視中將資料列分組
聚合也適用於以視圖為基礎的實體。 為檢視設定一個鍵欄位,讓 Data API 建構器能將其暴露為實體。
SQL 檢視
CREATE TABLE dbo.Employees (
id INT NOT NULL PRIMARY KEY,
name NVARCHAR(100) NOT NULL,
department NVARCHAR(50) NOT NULL,
title NVARCHAR(100) NOT NULL,
age INT NOT NULL
);
INSERT INTO dbo.Employees (id, name, department, title, age) VALUES
(1, N'Ada', N'Engineering', N'Developer', 29),
(2, N'Ben', N'Engineering', N'Architect', 41),
(3, N'Cora', N'Sales', N'Account manager', 34),
(4, N'Diego', N'Sales', N'Sales lead', 52),
(5, N'Ema', N'Support', N'Support engineer', 25),
(6, N'Finn', N'Support', N'Support lead', 38),
(7, N'Gia', N'Engineering', N'Engineering manager', 45);
CREATE VIEW dbo.EmployeeAgeReport
AS
SELECT id, department, age
FROM dbo.Employees;
| id | 部門 | 年齡 |
|---|---|---|
| 1 | 工程 | 二十九 |
| 2 | 工程 | 41 |
| 3 | Sales | 34 |
| 4 | Sales | 52 |
| 5 | Support | 25 |
| 6 | Support | 38 |
| 7 | 工程 | 45 |
將視圖設定為 id 鍵欄位:
dab add EmployeeAgeReport --source dbo.EmployeeAgeReport --source.type view --source.key-fields id --permissions "anonymous:read"
GraphQL 查詢
{
employeeAgeReports(orderBy: { department: ASC }) {
groupBy(fields: [department]) {
fields { department }
aggregations {
youngest: min(field: age)
oldest: max(field: age)
employeeCount: count(field: id)
}
}
}
}
產生的 SQL
SELECT TOP 100
[table0].[department] AS [department],
MIN([table0].[age]) AS [youngest],
MAX([table0].[age]) AS [oldest],
COUNT([table0].[id]) AS [employeeCount]
FROM [dbo].[EmployeeAgeReport] AS [table0]
WHERE 1 = 1
GROUP BY [table0].[department]
ORDER BY [table0].[department] ASC
FOR JSON PATH, INCLUDE_NULL_VALUES;
結果輸出
{
"data": {
"employeeAgeReports": {
"groupBy": [
{
"fields": {
"department": "Engineering"
},
"aggregations": {
"youngest": 29,
"oldest": 45,
"employeeCount": 3
}
},
{
"fields": {
"department": "Sales"
},
"aggregations": {
"youngest": 34,
"oldest": 52,
"employeeCount": 2
}
},
{
"fields": {
"department": "Support"
},
"aggregations": {
"youngest": 25,
"oldest": 38,
"employeeCount": 2
}
}
]
}
}
}
| 部門 | 最年輕的 | 最舊的 | 員工人數 |
|---|---|---|---|
| 工程 | 二十九 | 45 | 3 |
| Sales | 34 | 52 | 2 |
| Support | 25 | 38 | 2 |
聚合前的過濾列
使用集合查詢上的 filter,在 Data API builder 將來源資料列分組及彙總之前先限制其數量。
SQL 檢視
CREATE TABLE dbo.Employees (
id INT NOT NULL PRIMARY KEY,
name NVARCHAR(100) NOT NULL,
department NVARCHAR(50) NOT NULL,
title NVARCHAR(100) NOT NULL,
age INT NOT NULL
);
INSERT INTO dbo.Employees (id, name, department, title, age) VALUES
(1, N'Ada', N'Engineering', N'Developer', 29),
(2, N'Ben', N'Engineering', N'Architect', 41),
(3, N'Cora', N'Sales', N'Account manager', 34),
(4, N'Diego', N'Sales', N'Sales lead', 52),
(5, N'Ema', N'Support', N'Support engineer', 25),
(6, N'Finn', N'Support', N'Support lead', 38),
(7, N'Gia', N'Engineering', N'Engineering manager', 45);
CREATE VIEW dbo.EmployeeAgeReport
AS
SELECT id, department, age
FROM dbo.Employees;
| id | 部門 | 年齡 |
|---|---|---|
| 1 | 工程 | 二十九 |
| 2 | 工程 | 41 |
| 3 | Sales | 34 |
| 4 | Sales | 52 |
| 5 | Support | 25 |
| 6 | Support | 38 |
| 7 | 工程 | 45 |
GraphQL 查詢
{
employeeAgeReports(filter: { age: { gt: 30 } }, orderBy: { department: ASC }) {
groupBy(fields: [department]) {
fields { department }
aggregations {
youngest: min(field: age)
oldest: max(field: age)
employeeCount: count(field: id)
}
}
}
}
產生的 SQL
SELECT TOP 100
[table0].[department] AS [department],
MIN([table0].[age]) AS [youngest],
MAX([table0].[age]) AS [oldest],
COUNT([table0].[id]) AS [employeeCount]
FROM [dbo].[EmployeeAgeReport] AS [table0]
WHERE [table0].[age] > @param1
GROUP BY [table0].[department]
ORDER BY [table0].[department] ASC
FOR JSON PATH, INCLUDE_NULL_VALUES;
對於此查詢, @param1 為 30。
結果輸出
{
"data": {
"employeeAgeReports": {
"groupBy": [
{
"fields": {
"department": "Engineering"
},
"aggregations": {
"youngest": 41,
"oldest": 45,
"employeeCount": 2
}
},
{
"fields": {
"department": "Sales"
},
"aggregations": {
"youngest": 34,
"oldest": 52,
"employeeCount": 2
}
},
{
"fields": {
"department": "Support"
},
"aggregations": {
"youngest": 38,
"oldest": 38,
"employeeCount": 1
}
}
]
}
}
}
| 部門 | 最年輕的 | 最舊的 | 員工人數 |
|---|---|---|---|
| 工程 | 41 | 45 | 2 |
| Sales | 34 | 52 | 2 |
| Support | 38 | 38 | 1 |
使用 having 篩選群組
使用 having 在彙總後篩選彙總函式中的群組。 此模式對應到 SQL HAVING 子句。
SQL 表格
CREATE TABLE dbo.Products (
id INT NOT NULL PRIMARY KEY,
category NVARCHAR(50) NOT NULL,
price DECIMAL(10,2) NOT NULL
);
INSERT INTO dbo.Products (id, category, price) VALUES
(1, N'Electronics', 5000.00),
(2, N'Electronics', 10000.00),
(3, N'Furniture', 4000.00),
(4, N'Furniture', 8000.00),
(5, N'Books', 100.00),
(6, N'Books', 200.00);
| id | 分類 | 價格 |
|---|---|---|
| 1 | 電子產品 | 5000.00 |
| 2 | 電子產品 | 10000.00 |
| 3 | 傢俱 | 4000.00 |
| 4 | 傢俱 | 8000.00 |
| 5 | 書籍 | 100.00 |
| 6 | 書籍 | 200.00 |
GraphQL 查詢
{
products(orderBy: { category: ASC }) {
groupBy(fields: [category]) {
fields { category }
aggregations {
totalValue: sum(field: price, having: { gt: 10000 })
averagePrice: avg(field: price)
}
}
}
}
產生的 SQL
SELECT TOP 100
[table0].[category] AS [category],
SUM([table0].[price]) AS [totalValue],
AVG([table0].[price]) AS [averagePrice]
FROM [dbo].[Products] AS [table0]
WHERE 1 = 1
GROUP BY [table0].[category]
HAVING SUM([table0].[price]) > @param1
ORDER BY [table0].[category] ASC
FOR JSON PATH, INCLUDE_NULL_VALUES;
對於此查詢, @param1 為 10000。
結果輸出
{
"data": {
"products": {
"groupBy": [
{
"fields": {
"category": "Electronics"
},
"aggregations": {
"totalValue": 15000,
"averagePrice": 7500
}
},
{
"fields": {
"category": "Furniture"
},
"aggregations": {
"totalValue": 12000,
"averagePrice": 6000
}
}
]
}
}
}
| 分類 | 總值 | 平均價格 |
|---|---|---|
| 電子產品 | 15000 | 7500 |
| 傢俱 | 12000 | 6000 |
計算相異值數量
使用 distinct: true 搭配 count 來計算每個群組中的唯一值。
SQL 表格
CREATE TABLE dbo.Orders (
id INT NOT NULL PRIMARY KEY,
customer_id INT NOT NULL,
product_id INT NOT NULL
);
INSERT INTO dbo.Orders (id, customer_id, product_id) VALUES
(1, 101, 1),
(2, 101, 2),
(3, 101, 2),
(4, 101, 3),
(5, 101, 4),
(6, 101, 5),
(7, 102, 1),
(8, 102, 1),
(9, 102, 2),
(10, 102, 3);
| id | customer_id | product_id |
|---|---|---|
| 1 | 101 | 1 |
| 2 | 101 | 2 |
| 3 | 101 | 2 |
| 4 | 101 | 3 |
| 5 | 101 | 4 |
| 6 | 101 | 5 |
| 7 | 102 | 1 |
| 8 | 102 | 1 |
| 9 | 102 | 2 |
| 10 | 102 | 3 |
GraphQL 查詢
{
orders(orderBy: { customer_id: ASC }) {
groupBy(fields: [customer_id]) {
fields { customer_id }
aggregations {
uniqueProducts: count(field: product_id, distinct: true)
totalOrders: count(field: id)
}
}
}
}
產生的 SQL
SELECT TOP 100
[table0].[customer_id] AS [customer_id],
COUNT(DISTINCT ([table0].[product_id])) AS [uniqueProducts],
COUNT([table0].[id]) AS [totalOrders]
FROM [dbo].[Orders] AS [table0]
WHERE 1 = 1
GROUP BY [table0].[customer_id]
ORDER BY [table0].[customer_id] ASC
FOR JSON PATH, INCLUDE_NULL_VALUES;
結果輸出
{
"data": {
"orders": {
"groupBy": [
{
"fields": {
"customer_id": 101
},
"aggregations": {
"uniqueProducts": 5,
"totalOrders": 6
}
},
{
"fields": {
"customer_id": 102
},
"aggregations": {
"uniqueProducts": 3,
"totalOrders": 4
}
}
]
}
}
}
| customer_id | uniqueProducts | 訂單總數 |
|---|---|---|
| 101 | 5 | 6 |
| 102 | 3 | 4 |
常見的錯誤
- 在收藏欄位中選擇
groupBy。 不要將groupBy作為集合引數傳遞。 - 使用
aggregations,而不是aggregates。 - 使用
avg,而不是average。 - 使用欄位列舉值,例如
fields: [year]。 不要引用田地名稱。 - 不要在同一個集合查詢中選擇
items和groupBy。 - 當你依欄位分組時,只選擇物件內
fields相同的欄位。