Compartilhar via


Agregar dados com o GraphQL no construtor de API de Dados

O DAB (Construtor de API de Dados) dá suporte à agregação e ao agrupamento do GraphQL para bancos de dados da família SQL e do Azure Synapse Analytics (pool de SQL dedicado). As agregações permitem resumir campos numéricos e resultados de grupo sem escrever código de API personalizado. A agregação e groupBy não estão disponíveis para o Azure Cosmos DB para NoSQL, PostgreSQL ou MySQL.

Pré-requisitos

  • Banco de dados com suporte:
    • SQL Server 2016 ou posterior
    • Banco de Dados SQL do Azure
    • Instância Gerenciada de SQL do Azure
    • Microsoft Fabric SQL
    • Azure Synapse Analytics (somente pool de SQL dedicado)
  • CLI do construtor de API de Dados. Instalar a CLI
  • Um arquivo de configuração do DAB onde sua entidade é exposta por meio do GraphQL.
  • Um cliente GraphQL (por exemplo, Banana Cake Pop ou GraphQL Playground) para executar consultas.

Bancos de dados com suporte

Base de dados Suporte à agregação
SQL Server/SQL do Azure/MICROSOFT Fabric SQL ✅ Sim
Azure Synapse (pool de SQL dedicado) ✅ Sim
Azure Synapse (pool de SQL sem servidor) ❌ Não
PostgreSQL ❌ Não
MySQL ❌ Não
Azure Cosmos DB for NoSQL ❌ Não

Funções de agregação

O DAB dá suporte às seguintes funções de agregação:

Função Aplica-se a Description
sum Somente campos numéricos Total de todos os valores
average Somente campos numéricos Média de todos os valores
min Somente campos numéricos Valor mínimo
max Somente campos numéricos Valor máximo
count Qualquer campo Contagem de valores não nulos

Constraints

  • sum, averagee minmax só funcionam em tipos de dados numéricos (int, decimal, float etc.).
  • count funciona em qualquer tipo de dados, incluindo cadeias de caracteres e datas.
  • Se uma tabela não tiver colunas numéricas, o DAB não gerará nós de agregação para essa entidade. Você ainda pode usar count em campos não numéricos.

Modificadores opcionais

Modificador Propósito Example
distinct: true Contar somente valores únicos Contar clientes distintos
having: { ... } Filtrar grupos após a agregação Mostrar grupos com a soma > de 1000

Executar o runtime do DAB

Inicie o DAB com o arquivo de configuração para que o endpoint GraphQL esteja disponível.

dab start

Consultar resultados agregados

Esta seção apresenta um exemplo completo mostrando o esquema de tabela, a consulta GraphQL, o SQL gerado e a resposta JSON.

Esquema de tabela

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

Consulta GraphQL

Use o GraphQL para agrupar linhas e retornar valores agregados para campos numéricos.

{
  books(
    groupBy: { fields: ["year"] }
  ) {
    items {
      year
    }
    aggregates {
      pages {
        sum
        average
        min
        max
      }
    }
  }
}
  • groupBy.fields agrupa linhas pelas colunas especificadas.
  • aggregates expõe funções de agregação para campos numéricos (por exemplo, pages).
  • O esquema GraphQL expõe apenas agregações para campos que dão suporte a eles; use a introspecção de esquema em seu cliente para confirmar campos e funções de agregação disponíveis.

SQL gerado

O DAB converte a consulta GraphQL em 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

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

Os arrays items e aggregates estão alinhados por índice — o primeiro elemento em aggregates.pages corresponde ao primeiro grupo em items.

Agregar sem agrupamento

Calcular agregações em todas as linhas quando você omitir groupBy.

Consulta GraphQL

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

SQL gerado

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

Resposta JSON

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

Sem groupBy, a resposta retorna um único objeto (não uma matriz) porque todas as linhas são recolhidas em um único resultado.

Agrupar por um ou mais campos

Agrupar linhas por uma ou mais colunas e retornar agregações por grupo.

Esquema de tabela

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

Consulta GraphQL

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

SQL gerado

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

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

A resposta retorna matrizes para items e valores agregados na mesma ordem, para que você possa alinhar os grupos com seus valores agregados.

TER que filtrar resultados agregados

Use having para filtrar grupos após a agregação. Isso é equivalente à cláusula do HAVING SQL.

Esquema de tabela

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

Consulta GraphQL

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

SQL gerado

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

Resposta JSON

Somente as categorias em que a soma excede 10000 são retornadas:

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

Operadores HAVING

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

Observação

Cada having filtro se aplica de forma independente à sua função de agregação. Você não pode criar condições de agregação cruzada, como "soma > 1000 OU contagem < 10" em uma única consulta GraphQL.

DISTINCT em agregações

Contar valores exclusivos com distinct: true.

Esquema de tabela

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

Consulta GraphQL

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

SQL gerado

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

Resposta JSON

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

O primeiro count (com distinct: true) retorna produtos exclusivos por cliente. O segundo count retorna o total de pedidos.

Observação

Ao solicitar várias agregações no mesmo campo, o DAB as retorna na ordem solicitada. Use aliases (por exemplo, uniqueProducts: count(distinct: true)) para tornar as respostas autoexplicativas.

Combinar filtros com agregação

Aplique filter às linhas antes do agrupamento e having aos grupos após a agregação. Entender a ordem das operações é fundamental:

  1. Filtro (SQL WHERE) remove linhas antes do agrupamento
  2. Agrupar organiza as linhas restantes em grupos
  3. Agregar calcula soma/média/mínimo/máximo/contagem por grupo
  4. HAVING remove grupos que não correspondem à condição

Consulta GraphQL

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

SQL gerado

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

Dica

Use filter para excluir linhas antes da agregação. Use having para filtrar grupos após a agregação.

Usar aliases com agregações

Crie nomes de campo significativos usando aliases do GraphQL.

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

Introspecção de esquema

Use a introspecção para ver quais agregações estão disponíveis para uma entidade.

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

Campos numéricos expõem sum, average, min, maxe count. Campos não numéricos revelam count.

Dicas e limitações

  • Agregação e groupBy se aplicam apenas ao SQL Server, ao Azure SQL, ao Microsoft Fabric SQL e ao pool de SQL Dedicado do Azure Synapse.
  • Agregações são executadas em campos numéricos; count funciona em qualquer campo. Tabelas sem colunas numéricas expõem apenas count.
  • O agrupamento se aplica a campos na mesma entidade (sem groupBy entre entidades).
  • Agregações grandes podem ser caras; indexe as colunas que você utiliza no groupBy e filtre linhas antes de agrupar quando possível.
  • Crie índices em colunas usadas groupBy com frequência para melhorar o desempenho da consulta.

Resolução de problemas

Erro: o campo não dá suporte à agregação

Causa: usando sum, average, minou max em um campo não numérico.

Solução:

  • Use a introspecção de esquema para verificar os tipos de campo.
  • Use count para campos não numéricos.
  • Verifique os mapeamentos de campo se estiver usando nomes de campo personalizados.

Erro: nós de agregação não encontrados

Causa: a entidade não tem colunas numéricas.

Solução:

  • Verifique se o esquema da tabela tem pelo menos uma coluna numérica.
  • Utilize count agregações em campos não numéricos, se necessário.

Consultas de agregação lentas

Causa: tabelas grandes sem índices adequados.

Solução:

  • Crie índices em groupBy colunas.
  • Use filter para limitar linhas antes da agregação.
  • Use having para reduzir o número de grupos retornados.

Próxima etapa