Compartir a través de


Agregar datos con GraphQL en el generador de Data API

Data API Builder (DAB) admite la agregación y agrupación de GraphQL para bases de datos de la familia SQL y Azure Synapse Analytics (grupo de SQL dedicado). Las agregaciones permiten resumir campos numéricos y resultados de grupo sin escribir código de API personalizado. Agregación y groupBy no están disponibles para Azure Cosmos DB para NoSQL, PostgreSQL o MySQL.

Prerrequisitos

  • Base de datos admitida:
    • SQL Server 2016 o posterior
    • Azure SQL Database
    • Instancia Gestionada de Azure SQL
    • Microsoft Fabric SQL
    • Azure Synapse Analytics (solo grupo de SQL dedicado)
  • CLI para el generador de APIs de datos. Instalación de la CLI
  • Un archivo de configuración DAB con la entidad expuesta a través de GraphQL.
  • Un cliente GraphQL (por ejemplo, Banana Cake Pop o GraphQL Playground) para ejecutar consultas.

Bases de datos admitidas

Base de datos Soporte de agregaciones
SQL Server / Azure SQL / Microsoft Fabric SQL ✅ Sí
Azure Synapse (grupo de SQL dedicado) ✅ Sí
Azure Synapse (grupo de SQL sin servidor) ❌ No
PostgreSQL ❌ No
MySQL ❌ No
Azure Cosmos DB para NoSQL ❌ No

Funciones agregadas

DAB admite las siguientes funciones de agregado:

Función Se aplica a Description
sum Solo campos numéricos Total de todos los valores
average Solo campos numéricos Media de todos los valores
min Solo campos numéricos Valor mínimo
max Solo campos numéricos Valor máximo
count Cualquier campo Recuento de valores no nulos

Constraints

  • sum, average, miny max solo funcionan en tipos de datos numéricos (int, decimal, float, etc.).
  • count funciona en cualquier tipo de datos, incluidas cadenas y fechas.
  • Si una tabla no tiene columnas numéricas, DAB no genera nodos de agregación para esa entidad. Todavía puede usar count en campos no numéricos.

Modificadores opcionales

Modificador Propósito Example
distinct: true Solo contar valores únicos Recuento de clientes distintos
having: { ... } Filtrar grupos después de la agregación Mostrar los grupos con suma > 1000

Ejecutar el runtime de DAB

Inicie DAB con el archivo de configuración para que el punto de conexión de GraphQL esté disponible.

dab start

Consulta de resultados agregados

En esta sección se explica un ejemplo completo que muestra el esquema de tabla, la consulta GraphQL, la respuesta SQL generada y JSON.

Esquema de tabla

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

Consulta de GraphQL

Use GraphQL para agrupar filas y devolver valores agregados para campos numéricos.

{
  books(
    groupBy: { fields: ["year"] }
  ) {
    items {
      year
    }
    aggregates {
      pages {
        sum
        average
        min
        max
      }
    }
  }
}
  • groupBy.fields agrupa las filas por las columnas especificadas.
  • aggregates expone funciones de agregado para campos numéricos (por ejemplo, pages).
  • El esquema GraphQL solo expone agregados para campos que los admiten; use la introspección de esquema en el cliente para confirmar las funciones y campos agregados disponibles.

SQL generado

DAB convierte la consulta graphQL en 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

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

Las items matrices y aggregates se alinean por índice: el primer elemento de aggregates.pages corresponde al primer grupo de items.

Agregación sin agrupamiento

Calcule los agregados en todas las filas cuando omita groupBy.

Consulta de GraphQL

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

SQL generado

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

Respuesta JSON

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

Sin groupBy, la respuesta devuelve un único objeto (no una matriz) porque todas las filas se contraen en un resultado.

Agrupar por uno o varios campos

Agrupa las filas por una o varias columnas y devuelve agregados por grupo.

Esquema de tabla

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

Consulta de GraphQL

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

SQL generado

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

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

La respuesta devuelve matrices para items y agregados en el mismo orden para que pueda alinear grupos con sus valores agregados.

Tener que filtrar resultados agregados

Use having para filtrar grupos después de la agregación. Esto equivale a la cláusula de HAVING SQL.

Esquema de tabla

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

Consulta de GraphQL

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

SQL generado

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

Respuesta JSON

Solo se devuelven las categorías en las que la suma supera los 10000:

{
  "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 de 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 }

Nota:

Cada having filtro se aplica de forma independiente a su función de agregado. No se pueden crear condiciones de agregado cruzado como "suma > 1000 O recuento < 10" en una sola consulta GraphQL.

DISTINCT en agregaciones

Contar valores únicos con distinct: true.

Esquema de tabla

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

Consulta de GraphQL

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

SQL generado

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

Respuesta JSON

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

La primera count (con distinct: true) devuelve productos únicos por cliente. El segundo count devuelve el total de pedidos.

Nota:

Al solicitar varios agregados en el mismo campo, DAB los devuelve en el orden solicitado. Use alias (por ejemplo, uniqueProducts: count(distinct: true)) para hacer que las respuestas se documente automáticamente.

Combinación de filtros con agregación

Se aplica filter a las filas antes de la agrupación y having a los grupos después de la agregación. Comprender el orden de las operaciones es fundamental:

  1. Filter (SQL WHERE) quita las filas antes de la agrupación.
  2. Agrupar por recopila las filas restantes en grupos
  3. Aggregate calcula suma/promedio/mínimo/máximo/recuento por grupo
  4. Tener quita grupos que no coinciden con la condición

Consulta de GraphQL

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

SQL generado

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

Sugerencia

Use filter para excluir filas antes de la agregación. Use having para filtrar grupos después de la agregación.

Uso de alias con agregaciones

Cree nombres de campo significativos mediante alias graphQL.

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

Introspección de esquema

Use la introspección para ver qué agregados están disponibles para una entidad.

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

Los campos numéricos exponen sum, averagemin, , maxy count. Los campos no numéricos exponen count.

Sugerencias y limitaciones

  • Solo la agregación y groupBy se aplican a SQL Server, Azure SQL, Microsoft Fabric SQL y Azure Synapse Dedicated SQL pool.
  • Los agregados se ejecutan en campos numéricos; count funciona en cualquier campo. Las tablas sin columnas numéricas solo exponen count.
  • La agrupación se aplica a los campos de la misma entidad (sin groupBy entre entidades).
  • Las agregaciones grandes pueden ser costosas; indexe las columnas groupBy y filtre las filas antes de agruparlas cuando sea posible.
  • Cree índices en columnas usadas groupBy con frecuencia para mejorar el rendimiento de las consultas.

Solución de problemas

Error: Field no admite la agregación

Causa: Usar sum, average, mino max en un campo no numérico.

Solución:

  • Utilice introspección de esquema para comprobar los tipos de campo.
  • Se usa count para campos no numéricos.
  • Compruebe las asignaciones de campos si usa nombres de campo personalizados.

Error: No se encontraron nodos de agregación

Causa: La entidad no tiene columnas numéricas.

Solución:

  • Compruebe que el esquema de tabla tiene al menos una columna numérica.
  • Utilice count agregados en campos no numéricos si es necesario.

Consultas de agregación lentas

Causa: Tablas grandes sin índices adecuados.

Solución:

  • Cree índices en las columnas de groupBy.
  • Use filter para limitar las filas antes de la agregación.
  • Use having para reducir el número de grupos devueltos.

Paso siguiente