Agregar datos con GraphQL

Data API Builder admite la agregación de GraphQL para las entidades de la familia SQL Server y del grupo de SQL dedicado de Azure Synapse Analytics. Utilice el campo groupBy en una consulta de colección para calcular los valores sum, avg, min, max y count.

En los ejemplos de este artículo se usan entidades SQL Server y GraphQL con permiso de lectura. Data API builder genera instrucciones SQL como las que se muestran en cada escenario. Los valores de parámetro aparecen como parámetros de consulta en tiempo de ejecución. Las sumfunciones , avg, miny max se aplican a los campos numéricos. La count función funciona en cualquier campo.

Importante

La agregación no está disponible para Azure Cosmos DB para NoSQL, PostgreSQL o MySQL.

La agregación está habilitada de forma predeterminada. Para desactivarlo, configure enable-aggregation como false debajo de runtime.graphql en el archivo de configuración. Las consultas de agregación devuelven una página de grupos, 100 de forma predeterminada. Use el first argumento de la consulta de colección para cambiar el máximo, por ejemplo books(first: 500). Establezca el valor predeterminado con runtime.pagination.default-page-size.

Adiciones de esquema de GraphQL

Al habilitar la agregación, data API Builder agrega campos de agregación y tipos generados a cada colección de GraphQL admitida. Los nombres de tipo generados exactos son específicos de la entidad y son visibles a través de la introspección de GraphQL, pero la sintaxis de consulta es coherente entre entidades.

En los fragmentos de código siguientes se muestran formatos de sintaxis, no se completan las consultas de GraphQL.

groupBy

Devuelve filas agrupadas para la colección. Seleccione este miembro en lugar de items en una consulta de agregación.

<collection> { groupBy { ... } }

groupBy(fields: [...])

Enumera los campos de entidad por los que se va a agrupar. Omitir fields para agregar todas las filas a un grupo.

<collection> { groupBy(fields: [<field>, ...]) { ... } }

fields

Devuelve los valores de campo agrupados para cada fila del resultado de agregación.

groupBy(fields: [<field>]) { fields { <field> } }

aggregations

Contiene la selección de funciones de agregación para cada grupo.

groupBy { aggregations { <alias>: <function>(field: <field>) } }

sum, avg, min y max.

Agregar campos numéricos.

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

Cuenta los valores de un campo.

aggregations { <alias>: count(field: <field>) }

field

Identifica el campo de la entidad que se va a usar para la agregación. Los nombres de campo son valores de enumeración, no cadenas.

<function>(field: <field>)

having

Filtra los grupos después de que Data API Builder calcule el valor agregado.

aggregations { <alias>: <function>(field: <field>, having: { <operator>: <value> }) }

distinct

Cuenta valores únicos cuando se utiliza con count.

aggregations { <alias>: count(field: <field>, distinct: true) }

Data API builder genera tipos GraphQL específicos de la entidad para estos miembros, incluidos tipos de fila agrupada, tipos de campo agrupado, tipos de selección de agregados, enumeraciones de campo y tipos de entrada having. No es necesario asignar un nombre a esos tipos generados en una consulta.

Samples

Los siguientes ejemplos muestran la tabla SQL, la consulta GraphQL, el SQL resultante y la salida resultante correspondientes a patrones de agregación comunes.

Agregar todas las filas de una tabla

Use este patrón cuando desee una fila de resumen para toda la entidad.

Tabla 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 año Páginas
1 Conceptos básicos de GraphQL 2023 120
2 API avanzadas 2023 450
3 Patrones de datos 2023 390
4 API en la nube 2024 140
5 Componentes internos del entorno de ejecución 2024 510
6 Optimización de consultas 2024 250

Consulta de 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 resultante

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;

Salida resultante

{
  "data": {
    "books": {
      "groupBy": [
        {
          "aggregations": {
            "totalPages": 1860,
            "averagePages": 310,
            "shortestBook": 120,
            "longestBook": 510,
            "bookCount": 6
          }
        }
      ]
    }
  }
}
totalPages averagePages shortestBook longestBook bookCount
1860 310 120 510 6

Agrupar filas por un campo

Utilice groupBy(fields: [...]) para devolver una fila agregada por cada valor de campo. Los nombres de campo son valores de enumeración graphQL, no cadenas.

Tabla 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 año Páginas
1 Conceptos básicos de GraphQL 2023 120
2 API avanzadas 2023 450
3 Patrones de datos 2023 390
4 API en la nube 2024 140
5 Aspectos internos del entorno de ejecución 2024 510
6 Optimización de consultas 2024 250

Consulta de GraphQL

{
  books(orderBy: { year: ASC }) {
    groupBy(fields: [year]) {
      fields { year }
      aggregations {
        totalPages: sum(field: pages)
        averagePages: avg(field: pages)
      }
    }
  }
}

SQL resultante

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;

Salida resultante

{
  "data": {
    "books": {
      "groupBy": [
        {
          "fields": {
            "year": 2023
          },
          "aggregations": {
            "totalPages": 960,
            "averagePages": 320
          }
        },
        {
          "fields": {
            "year": 2024
          },
          "aggregations": {
            "totalPages": 900,
            "averagePages": 300
          }
        }
      ]
    }
  }
}
año totalPages promedio de páginas
2023 960 320
2024 900 300

Agrupar filas en una vista

La agregación también funciona para entidades basadas en vistas. Configure un campo de clave para la vista para que data API Builder pueda exponerlo como una entidad.

vista 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 departamento edad
1 Ingeniería 29
2 Ingeniería 41
3 Ventas 34
4 Ventas 52
5 Apoyo 25
6 Apoyo 38
7 Ingeniería 45

Configure la vista con id como campo de clave:

dab add EmployeeAgeReport --source dbo.EmployeeAgeReport --source.type view --source.key-fields id --permissions "anonymous:read"

Consulta de GraphQL

{
  employeeAgeReports(orderBy: { department: ASC }) {
    groupBy(fields: [department]) {
      fields { department }
      aggregations {
        youngest: min(field: age)
        oldest: max(field: age)
        employeeCount: count(field: id)
      }
    }
  }
}

SQL resultante

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;

Salida resultante

{
  "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
          }
        }
      ]
    }
  }
}
departamento más joven más antiguo numeroDeEmpleados
Ingeniería 29 45 3
Ventas 34 52 2
Apoyo 25 38 2

Filtrar filas antes de la agregación

Use filter en la consulta de colección para limitar las filas de origen antes de que Data API Builder las agrupe y agregue.

vista 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 departamento edad
1 Ingeniería 29
2 Ingeniería 41
3 Ventas 34
4 Ventas 52
5 Apoyo 25
6 Apoyo 38
7 Ingeniería 45

Consulta de 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 resultante

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;

Para esta consulta, @param1 es 30.

Salida resultante

{
  "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
          }
        }
      ]
    }
  }
}
departamento más joven más antiguo Número de empleados
Ingeniería 41 45 2
Ventas 34 52 2
Apoyo 38 38 1

Filtrado de grupos mediante having

Use having en una función de agregado para filtrar grupos después de la agregación. Este patrón se asigna a una cláusula SQL HAVING .

Tabla 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 category precio
1 Electrónica 5000,00
2 Electrónica 10000,00
3 Mobiliario 4000.00
4 Mobiliario 8000.00
5 Libros 100.00
6 Libros 200.00

Consulta de GraphQL

{
  products(orderBy: { category: ASC }) {
    groupBy(fields: [category]) {
      fields { category }
      aggregations {
        totalValue: sum(field: price, having: { gt: 10000 })
        averagePrice: avg(field: price)
      }
    }
  }
}

SQL resultante

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;

Para esta consulta, @param1 es 10000.

Salida resultante

{
  "data": {
    "products": {
      "groupBy": [
        {
          "fields": {
            "category": "Electronics"
          },
          "aggregations": {
            "totalValue": 15000,
            "averagePrice": 7500
          }
        },
        {
          "fields": {
            "category": "Furniture"
          },
          "aggregations": {
            "totalValue": 12000,
            "averagePrice": 6000
          }
        }
      ]
    }
  }
}
category valor total precio medio
Electrónica 15000 7500
Mobiliario 12000 6000

Recuento de valores distintos

Use distinct: true con count para contar valores únicos en cada grupo.

Tabla 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

Consulta de 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 resultante

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;

Salida resultante

{
  "data": {
    "orders": {
      "groupBy": [
        {
          "fields": {
            "customer_id": 101
          },
          "aggregations": {
            "uniqueProducts": 5,
            "totalOrders": 6
          }
        },
        {
          "fields": {
            "customer_id": 102
          },
          "aggregations": {
            "uniqueProducts": 3,
            "totalOrders": 4
          }
        }
      ]
    }
  }
}
customer_id uniqueProducts Total de pedidos
101 5 6
102 3 4

Errores comunes

  • Seleccione groupBy dentro del campo de colección. No pases groupBy como argumento de tipo colección.
  • Use aggregations, no aggregates.
  • Use avg, no average.
  • Use valores de enumeración de campo, como fields: [year]. No ponga entre comillas los nombres de campo.
  • No seleccione items y groupBy en la misma consulta de recopilación.
  • Al agrupar por campos, seleccione solo los mismos campos dentro del fields objeto .