数据 API 生成器 (DAB) 支持 SQL 系列数据库和 Azure Synapse Analytics(专用 SQL 池)的 GraphQL 聚合和分组。 聚合使你无需编写自定义 API 代码即可汇总数值字段和分组结果。 聚合, groupBy 不适用于 Azure Cosmos DB for NoSQL、PostgreSQL 或 MySQL。
先决条件
- 支持的数据库:
- SQL Server 2016 或更高版本
- Azure SQL 数据库
- Azure SQL 托管实例
- Microsoft Fabric SQL
- Azure Synapse Analytics (仅限专用 SQL 池)
- 数据 API 生成器 CLI。 安装 CLI
- 包含您的通过 GraphQL 公开的实体的 DAB 配置文件。
- 用于运行查询的 GraphQL 客户端(例如 Banana Cake Pop 或 GraphQL Playground)。
支持的数据库
| 数据库 | 聚合支持 |
|---|---|
| SQL Server/Azure SQL/Microsoft Fabric SQL | ✅ 是 |
| Azure Synapse (专用 SQL 池) | ✅ 是 |
| Azure Synapse (无服务器 SQL 池) | ❌ 否 |
| PostgreSQL | ❌ 否 |
| MySQL | ❌ 否 |
| Azure Cosmos DB for NoSQL | ❌ 否 |
聚合函数
DAB 支持以下聚合函数:
| 功能 | 适用于 | Description |
|---|---|---|
sum |
仅限数值字段 | 所有值的总数 |
average |
仅限数值字段 | 所有值的平均值 |
min |
仅限数值字段 | 最小值 |
max |
仅限数值字段 | 最大值 |
count |
任何字段 | 非空值的计数 |
限制条件
-
sum、average、min和max仅处理数值数据类型(int、decimal、float 等)。 -
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 }
]
}
}
}
}
items和aggregates数组按索引对齐,第items数组的第一个元素对应于aggregates数组的第一个组。
不分组聚合
省略 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筛选聚合结果
使用 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 }
]
}
}
}
}
HAVING 运算符
| 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 OR count < 10”等交叉聚合条件。
聚合中的 DISTINCT
使用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 应用于各组。 了解作顺序至关重要:
-
筛选 (SQL
WHERE) 在分组之前删除行 - 分组 将剩余行收集到组中
- 聚合 对每个组计算总和/平均值/最小值/最大值/计数
- HAVING 删除与条件不匹配的组
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
}
}
}
}
架构反省
使用内省查看哪些聚合可用于实体。
{
__type(name: "BooksAggregates") {
fields {
name
type { name }
}
}
}
数值字段公开 sum、 average、 min、 max和 count。 非数值字段暴露 count。
提示和限制
- 聚合和
groupBy仅应用于 SQL Server、Azure SQL、Microsoft Fabric SQL 和 Azure Synapse 专用 SQL 池。 - 聚合在数值字段上运行;
count适用于任何字段。 没有数值列的表仅公开count。 - 分组适用于同一实体上的字段(不支持跨实体 groupBy)。
- 大型聚合可能会开销大。在可能的情况下,在 groupBy 列上编制索引并对行进行筛选后再进行分组。
- 针对常用
groupBy列创建索引以提高查询性能。
Troubleshooting
错误:字段不支持聚合
原因:在非数值字段中使用sum、average、min或max。
解决方案:
- 使用架构反省来验证字段类型。
- 对非数值字段使用
count。 - 如果使用自定义字段名称,请检查字段映射。
错误:找不到聚合节点
原因:实体没有数值列。
解决方案:
- 验证表模式是否至少包含一个数值列。
- 如果需要,请对非数值字段使用
count聚合。
聚合查询速度缓慢
原因:没有正确索引的大型表。
解决方案:
- 在
groupBy列上创建索引。 - 使用
filter来限制聚合前的行。 - 用于
having减少返回的组数。