用于 GraphQL 的 API 中的聚合

使用 GraphQL 聚合将 Microsoft Fabric 数据转换为可作的见解。 可以要求 Fabric 对数据进行分组和计算服务器端摘要,而不是检索成千上万的单个记录并在应用程序中进行处理,从而显著提高性能并减少数据传输。

GraphQL 聚合的工作方式类似于 SQL GROUP BY 操作,但通过 GraphQL API 实现。 可以计算每个类别的项目数、计算收入总计、查找平均评级,或确定 Lakehouse 和仓库表中的最小值/最大值(全部在单个高效查询中)。

主要优势:

  • 服务器端处理:利用 Fabric 的优化查询引擎进行计算
  • 减少数据传输:获取摘要而不是原始记录
  • 单个查询效率:用一个聚合替换多个客户端操作

本指南介绍如何使用实际的电子商务示例生成聚合查询,涵盖从基本分组到高级函数和重要限制的所有内容。

谁应使用聚合

GraphQL 聚合对于以下方面非常有用:

  • 应用程序开发人员 构建需要汇总Fabric数据的定制仪表板和分析应用程序
  • 数据工程师 创建数据 API,以从 Fabric 数据湖和仓库中提供预先计算的指标和关键绩效指标 (KPI)
  • BI 开发人员 构建自定义分析解决方案,以使用聚合构造数据补充 Power BI
  • 集成开发人员 创建需要 Fabric 摘要统计信息的应用程序和工作流
  • 数据分析师 构建自助分析解决方案,这些解决方案需要从 Fabric 数据中获取分组和聚合的见解

如果要检索数据以显示图表、计算总计、生成报表或分析趋势,聚合可以显著提高应用程序的性能并减少数据传输。

可以回答的常见业务问题

GraphQL 聚合擅长回答有关 Fabric 数据的分析问题:

  • 计数和分组“每个类别中的产品数?”“每月订单数?”
  • 财务计算“按区域划分的总收入是多少?”“按客户细分的平均订单值?”
  • 性能指标“每个类别中最高和最低评级的产品是什么?
  • 客户见解“本月访问的唯一客户数?”“哪些城市拥有最活跃的用户?”

这些查询非常适合用于生成仪表板、生成报表和支持分析应用程序,需要汇总的数据,而不是单个记录。

先决条件

在使用 GraphQL 聚合之前,请确保具备:

  • 具有适当权限的 Microsoft Fabric 工作区
  • 包含要聚合数据的表的 Lakehouse 或仓库
  • 为您的 Fabric 项目配置的 GraphQL 终结点的 API
  • 基本熟悉 GraphQL 查询语法

运行这些查询的位置

快速入门:使用 Fabric 工作区中 GraphQL 编辑器的 API 测试本文中的所有示例。 编辑器提供架构浏览、查询验证和即时结果。

对于应用程序:使用任何用于编程语言的 GraphQL 客户端库,将查询作为 HTTP POST 请求发送到 GraphQL 终结点。

用于开发:GraphQL Playground、Insomnia 或 Postman 等工具适用于查询开发和测试。

注释

本文中的示例一旦完成了对 GraphQL 终结点的 API 配置,就可以直接复制运行。 出于简洁考虑,一些示例已被缩短,可能需要根据您的特定模式进行调整。

示例方案:Fabric 中的电子商务数据

对于本指南,我们使用存储在Microsoft Fabric lakehouse 或仓库中的虚构电子商务数据集。 此方案演示如何使用 GraphQL 聚合分析零售数据。

在此示例中,产品数据属于类别,每个 Product 包含诸如价格和评级之类的字段(这些数值非常适合用于聚合),并与 Category 存在关系。 通过 Fabric 的用于 GraphQL 的 API 公开这些表时,生成的架构可能如下所示:

type Category {
  id: ID!
  name: String!
  products: [Product!]!  # one-to-many relationship
}

type Product {
  id: Int!
  name: String!
  price: Float!
  rating: Int!
  category_id: Int!
  category: Category!  # many-to-one relationship
}

type ProductResult { # automatically generated, adding groupBy capabilities
  items: [Product!]!
  endCursor: String
  hasNextPage: Boolean!
  groupBy(fields: [ProductScalarFields!]): [ProductGroupBy!]!
}

type Query {
  products(
    first: Int
    after: String
    filter: ProductFilterInput
    orderBy: ProductOrderByInput
  ): ProductResult!
}

在此示例中, products 查询可以返回常规项列表,或者如果使用 groupBy 聚合结果。 让我们专注于使用此查询的groupBy和聚合功能。

注释

不能在同一查询中检索普通项和分组结果。 有关更多详细信息,请参阅 聚合和原始项互斥

可用的聚合函数

可用的确切功能取决于实现,但常见的聚合操作包括:

  • count – 组中的记录计数(或字段的非空值的数量)。
  • sum – 数值字段中所有值的和。
  • avg – 数值字段中值的平均值(平均值)。
  • min – 字段中的最小值。
  • max – 字段中的最大值。

在 GraphQL 聚合中,可以指定函数名称和目标字段,如示例count(field: id)sum(field: price)等所示。每个函数返回一个对象,允许你选择应用于的一个或多个字段。

注释

在 Microsoft Fabric 的 GraphQL API 中,聚合操作(例如countsumavgminmax)当前仅适用于数值型或定量型字段(整数、浮点数)。 不能直接在文本或日期字段上使用它们。 例如,无法计算字符串字段的“平均值”。 在 Fabric 的未来更新中,可能会添加对其他数据类型(例如文本串联或词典最小/最大值)执行聚合的支持。

聚合查询基础知识

若要在 Fabric 的 GraphQL API 中执行聚合,请在查询中指定一个 groupBy 参数,以定义如何对数据进行分组,并在结果中请求聚合字段(如计数或总和)。 Fabric 的 GraphQL 引擎会针对基础 Lakehouse 或仓库表有效地处理这些查询,并返回包含其键值和计算聚合指标的分组记录的列表。

示例 1:对每个类别的产品进行计数

让我们按类别对产品进行分组,并计算每个组中的产品数量。 查询可能如下所示:

query {
  products {
   groupBy(fields: [category_id]) 
   {
      fields {
         category_id # grouped key values
      }
      aggregations {
        count(field: id) # count of products in each group (count of "id")
     } 
   }
  }
}

在本查询中:

  • groupBy(fields: [category_id]) 告知 Fabric GraphQL 引擎按 category_id 字段对产品进行分组。
  • 在结果选择中,你请求group,并对count字段执行id聚合。 使用 id 能有效统计该组中的产品。

结果如下所示: 响应中的每个项都表示一个类别组。 对象 groupBy 包含分组键。 在这里,它包括 category_id 该值,并提供 count { id } 该类别中的产品数:

{
  "data": {
    "products": {
      "groupBy": [
        {
          "fields": {
            "category_id": 1
          },
          "aggregations": {
            "count": 3
          }
        },
        {
          "fields": {
            "category_id": 2
          },
          "aggregations": {
            "count": 2
          }
        },
        // Sample shortened for brevity
      ]
    }
  }
}

此输出告诉我们类别 1 有三个产品,第 2 类有 2 个,等等。

示例 2:求和和平均值

我们可以在一个查询中请求多个聚合指标。 假设我们想要,对于每个类别,所有产品的总价格和平均评级:

query {
  products {
   groupBy(fields: [category_id]) 
   {
      fields {
         category_id
      }
     aggregations {
        count(field: id)   # number of products in the category
        sum(field: price)  # sum of all product prices in the category
        avg(field: rating) # average rating of products in the category
     } 
   }
  }
}

此查询将返回以下结果:

{
  "data": {
    "products": {
      "groupBy": [
        {
          "fields": {
            "category_id": 1
          },
          "aggregations": {
            "count": 3,
            "sum": 2058.98,
            "avg": 4
          }
        },
        {
          "fields": {
            "category_id": 2
          },
          "aggregations": {
            "count": 2,
            "sum": 109.94,
            "avg": 4
          }
        },
        ...
      ]
    }
  }
}

每个组对象包括类别和计算聚合,例如产品数、价格之和以及该类别中的平均分级。

示例 3:按多个字段分组

可以按多个字段进行分组以获取多级分组。 例如,如果你的产品有一个 rating 字段,则可以按两者 category_id 进行分组, rating 然后计算该组的平均值 price

query {
  products {
   groupBy(fields: [category_id, rating])
   {
      fields {
         category_id
         rating
      }
     aggregations {
        avg(field: price)
     }
   }
  }
}

这将按类别 分级的唯一组合对产品进行分组,如下所示:

 {
    "fields": {
        "category_id": 10,
        "rating": 4
    },
    "aggregations": {
        "avg": 6.99
    }
}

依此类推处理数据中的每个类别评分对。

小窍门

按多个字段分组时,显式排序对于可预测结果尤为重要。 请参阅 “对分组结果进行排序需要显式指定顺序”

示例 4:使用独特的

聚合功能支持 不同的 修饰符来计算或考虑唯一值。 例如,若要了解产品集合中存在多少个不同类别,可以使用非重复计数:

query {
  products {
   groupBy(fields: [category_id]) 
   {
      fields {
         category_id
      }
     aggregations {
        count(field: id, distinct: true) 
     } 
   }
  }
}

此查询返回一个结果,其中包含每个类别的唯一产品数。 结果如下所示:

{
  "data": {
    "products": {
      "groupBy": [
        {
          "fields": {
            "category_id": 1
          },
          "aggregations": {
            "count": 3
          }
        },
        {
          "fields": {
            "category_id": 2
          },
          "aggregations": {
            "count": 2
          }
        },
        ...
      ]
    }
  }
}

小窍门

有关何时以及如何适当地使用去重的更多指南,请参阅适当地使用去重聚合

示例 5:使用别名

可以为聚合创建别名,以便为聚合结果提供有意义的易于理解的名称。 例如,可以将上一示例中的聚合命名为distinctProductCategoryCount,因为它通过计数独特的产品类别来更好地理解结果。

query {
  products {
   groupBy(fields: [category_id]) 
   {
      fields {
         category_id
      }
     aggregations {
        distinctProductCategoryCount: count(field: id, distinct: true) 
     } 
   }
  }
}

结果与自定义别名类似,但更有意义:

{
  "data": {
    "products": {
      "groupBy": [
        {
          "fields": {
            "category_id": 1
          },
          "aggregations": {
            "distinctProductCategoryCount": 3
          }
        },
        {
          "fields": {
            "category_id": 2
          },
          "aggregations": {
            "distinctProductCategoryCount": 2
          }
        },
        ...
      ]
    }
  }
}

示例 6:使用 having 子句

可以使用 having 子句筛选聚合结果。 例如,可以修改前面的示例,以仅返回大于两个的结果:

query {
  products {
   groupBy(fields: [category_id]) 
   {
      fields {
         category_id
      }
     aggregations {
        distinctProductCategoryCount: count(field: id, distinct: true, having:  {
           gt: 2
        }) 
     } 
   }
  }
}

结果返回一个值,其唯一类别包含两个以上的产品:

{
  "data": {
    "products": {
      "groupBy": [
        {
          "fields": {
            "category_id": 1
          },
          "aggregations": {
            "distinctProductCategoryCount": 3
          }
        }
      ]
    }
  }
}

限制和最佳做法

在 Microsoft Fabric 的用于 GraphQL 的 API 中使用聚合时,需要考虑一些重要的规则和限制。 遵循这些最佳做法并了解这些限制,可以生成有效的 GraphQL 聚合查询,从而生成强大的见解,同时确保可预测的结果,尤其是在使用大型数据集或实现分页时。

聚合功能对于报告和分析用例非常有用,但它确实需要仔细构建查询。 始终仔细核对groupBy字段是否与所选的输出字段一致,为了实现可预测的顺序特别是在分页时,请添加排序,并根据数据类型适当地使用去重和聚合函数。

以下部分介绍需要了解的三个关键领域: 聚合和原始项互斥排序分组结果需要显式排序,并 适当地使用不同的聚合

聚合和原始项互斥

目前,不能 同时检索同一查询中已分组的摘要数据和原始项列表。 在查询中使用 groupBy 时,API 会切换到“聚合模式”,并仅返回分组结果。 此设计使响应结构明确 - 每个查询都处于“聚合模式”或“列表项模式”中,但绝不是两者。

这在实践中的工作原理:

查询 products(...) 返回以下任一项:

  • 在未使用 groupBy 时的单个产品列表
  • 分组结果列表,其中包含聚合数据(使用 groupBy 时)

请注意上面的聚合示例中,响应包含 groupBy 和聚合字段,但缺少通常 items 的产品列表。

如果同时尝试以下两种情况,会发生什么情况:

如果尝试在同一查询中请求普通项和组,GraphQL 引擎将返回错误或不允许该选择。

解决方法:

如果需要原始数据和聚合数据,请运行两个单独的查询:一个用于原始数据,另一个用于聚合数据。 此方法可让你完全控制这两个数据集,并且可以根据特定的缓存和性能要求进行优化。

对分组结果进行排序需要显式排序

除非指定显式排序,否则聚合组将按不可预知的顺序返回。 始终使用 orderBysort 参数来确保一致、有意义的结果。

为什么显式排序很重要:

  • 不可预知的默认顺序:如果没有 orderBy,组可能会以任意数据库确定的顺序返回
  • 分页要求:稳定排序顺序对于一致的分页行为至关重要
  • 用户体验:可预测排序可改善数据解释和应用程序可靠性

必须指定排序:

  • groupBy 字段中没有主键:如果分组字段不包含主键,则必须添加 orderBy
  • 非唯一分组键:按类别名称或日期等字段分组时
  • 分页方案:当您计划使用限制/偏移分页或游标分页时

最佳做法:

  • 按照聚合值排序(例如,从最高计数开始)以获得分析见解
  • 对基于类别的分组使用字母排序
  • 组合多个排序条件以满足复杂的排序需求

适当地使用不同的聚合

distinct修饰符在执行聚合之前会消除重复值,确保在数据包含重复项时进行准确的计算。

常见用例:

  • 唯一计数count(field: category_id, distinct: true) 计算每个组中存在的不同类别数
  • 去重后的总和sum(field: price, distinct: true) 每个组仅添加一次每个唯一的价格
  • 联接场景:当产品由于表联接出现多次时,Distinct 确保每个项目只被计数一次。

何时使用唯一:

  • 你的数据包含合法重复项,这些重复项将会影响计算结果。
  • 你正在处理联接的表,这些表创建了重复的行。
  • 需要计算唯一值而不是总出现次数

性能注意事项:

不同的操作需要更多的处理。 仅在必要时使用,以确保数据准确性。