Partager via


Agréger des données avec GraphQL dans le générateur d’API de données

Le générateur d’API de données (DAB) prend en charge l’agrégation et le regroupement GraphQL pour les bases de données de famille SQL et Azure Synapse Analytics (pool SQL dédié). Les agrégations vous permettent de résumer les champs numériques et les résultats de groupe sans écrire de code API personnalisé. L’agrégation et groupBy ne sont pas disponibles pour Azure Cosmos DB pour NoSQL, PostgreSQL ou MySQL.

Prerequisites

  • Base de données prise en charge :
    • SQL Server 2016 ou ultérieur
    • Azure SQL Database
    • Azure SQL Managed Instance (Instance gérée Azure SQL)
    • Microsoft Fabric SQL
    • Azure Synapse Analytics (pool SQL dédié uniquement)
  • CLI du générateur d'API de données. Installer l’interface CLI
  • Fichier de configuration DAB avec votre entité exposée via GraphQL.
  • Un client GraphQL (par exemple, Banana Cake Pop ou GraphQL Playground) pour exécuter des requêtes.

Bases de données prises en charge

Base de données Prise en charge de l’agrégation
SQL Server / Azure SQL / Microsoft Fabric SQL ✅ Oui
Azure Synapse (pool SQL dédié) ✅ Oui
Azure Synapse (SQL pool serverless) ❌ Non
PostgreSQL ❌ Non
MySQL ❌ Non
Azure Cosmos DB pour NoSQL ❌ Non

Fonctions d'agrégation

DAB prend en charge les fonctions d’agrégation suivantes :

Fonction S’applique à Descriptif
sum Champs numériques uniquement Total de toutes les valeurs
average Champs numériques uniquement Moyenne de toutes les valeurs
min Champs numériques uniquement Valeur minimale
max Champs numériques uniquement Valeur maximale
count N’importe quel champ Nombre de valeurs non nulles

Constraints

  • sum, average, minet max fonctionne uniquement sur les types de données numériques (int, decimal, float, etc.).
  • count fonctionne sur n’importe quel type de données, y compris les chaînes et les dates.
  • Si une table n’a pas de colonnes numériques, DAB ne génère pas de nœuds d’agrégation pour cette entité. Vous pouvez toujours utiliser count sur des champs non numériques.

Modificateurs facultatifs

Modificateur Objectif Example
distinct: true Compter les valeurs uniques uniquement Compter des clients distincts
having: { ... } Filtrer les groupes après l’agrégation Afficher les groupes avec la somme > 1000

Exécuter le runtime DAB

Démarrez DAB avec votre fichier de configuration afin que le point de terminaison GraphQL soit disponible.

dab start

Résultats agrégés des requêtes

Cette section décrit un exemple complet montrant le schéma de table, la requête GraphQL, la réponse SQL générée et JSON.

Schéma de table

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

Requête GraphQL

Utilisez GraphQL pour regrouper des lignes et retourner des valeurs d’agrégation pour les champs numériques.

{
  books(
    groupBy: { fields: ["year"] }
  ) {
    items {
      year
    }
    aggregates {
      pages {
        sum
        average
        min
        max
      }
    }
  }
}
  • groupBy.fields regroupe les lignes par les colonnes spécifiées.
  • aggregates expose les fonctions d’agrégation pour les champs numériques (par exemple, pages).
  • Le schéma GraphQL expose uniquement les agrégats pour les champs qui les prennent en charge ; utilisez l’introspection de schéma dans votre client pour confirmer les champs et fonctions d’agrégation disponibles.

SQL généré

DAB traduit la requête 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

Réponse 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 }
        ]
      }
    }
  }
}

Les tableaux items et aggregates s’alignent par index : le premier élément de aggregates.pages correspond au premier groupe de items.

Agréger sans regroupement

Calculez les agrégats sur toutes les lignes lorsque vous omettez groupBy.

Requête GraphQL

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

SQL généré

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

Réponse JSON

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

Sans groupBy, la réponse retourne un objet unique (et non un tableau), car toutes les lignes s’réduisent en un résultat.

Regrouper par un ou plusieurs champs

Regroupez les lignes par une ou plusieurs colonnes et renvoyez des agrégats par groupe.

Schéma de table

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

Requête GraphQL

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

SQL généré

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

Réponse 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 réponse retourne des tableaux pour items et des agrégats dans le même ordre afin de pouvoir aligner les groupes avec leurs valeurs agrégées.

HAVING pour filtrer les résultats agrégés

Permet having de filtrer des groupes après l’agrégation. Cela équivaut à la clause de HAVING SQL.

Schéma de table

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

Requête GraphQL

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

SQL généré

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

Réponse JSON

Seules les catégories où la somme dépasse 1 0000 sont retournées :

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

OPÉRATEURS HAVING

Operator Équivalent 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 }

Note

Chaque having filtre s’applique indépendamment à sa fonction d’agrégation. Vous ne pouvez pas créer de conditions d’agrégation croisée comme « sum > 1000 OR count < 10 » dans une seule requête GraphQL.

DISTINCT dans les agrégations

Compter les valeurs uniques avec distinct: true.

Schéma de table

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

Requête GraphQL

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

SQL généré

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

Réponse JSON

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

Le premier count (avec distinct: true) retourne des produits uniques par client. La deuxième count retourne le total des commandes.

Note

Lors de la demande de plusieurs agrégats sur le même champ, DAB les retourne dans l’ordre demandé. Utilisez des alias (par exemple uniqueProducts: count(distinct: true)) pour documenter automatiquement les réponses.

Combiner des filtres avec l’agrégation

Appliquez filter aux lignes avant le regroupement et having aux groupes après l’agrégation. Comprendre l’ordre des opérations est essentiel :

  1. Filter (SQL WHERE) supprime les lignes avant le regroupement
  2. Group by regroupe les lignes restantes dans des groupes
  3. L’agrégation calcule la somme/moyenne/minimum/maximum/nombre par groupe
  4. Having supprime les groupes qui ne correspondent pas à la condition.

Requête GraphQL

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

SQL généré

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

Conseil / Astuce

Permet filter d’exclure des lignes avant l’agrégation. Permet having de filtrer des groupes après l’agrégation.

Utiliser des alias avec des agrégations

Créez des noms de champs explicites à l’aide d’alias GraphQL.

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

Introspection de schéma

Utilisez l’introspection pour voir quels agrégats sont disponibles pour une entité.

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

Les champs numériques exposent sum, , averagemin, maxet count. Les champs non numériques exposent count.

Conseils et limitations

  • L'agrégation et groupBy s'appliquent uniquement à SQL Server, Azure SQL, Microsoft Fabric SQL, et au pool SQL dédié Azure Synapse.
  • Les agrégats s’exécutent sur des champs numériques ; count fonctionne sur n’importe quel champ. Les tables sans colonnes numériques n’exposent que count.
  • Le regroupement s’applique aux champs sur la même entité (aucun groupe d’entités inter-entitésBy).
  • Les agrégations volumineuses peuvent être coûteuses ; indexez vos colonnes groupBy et filtrez les lignes avant de les regrouper lorsque cela est possible.
  • Créez des index sur des colonnes fréquemment utilisées groupBy pour améliorer les performances des requêtes.

Résolution des problèmes

Erreur : Le champ ne prend pas en charge l’agrégation

Cause : Utilisation sum, , averageminou max sur un champ non numérique.

Solution:

  • Utilisez l’introspection de schéma pour vérifier les types de champs.
  • Utiliser count pour les champs non numériques.
  • Vérifiez les mappages de champs si vous utilisez des noms de champs personnalisés.

Erreur : Nœuds d’agrégation introuvables

Cause : l’entité n’a pas de colonnes numériques.

Solution:

  • Vérifiez que le schéma de table comporte au moins une colonne numérique.
  • Utilisez count pour agréger des champs non numériques si nécessaire.

Requêtes d’agrégation lentes

Cause : tables volumineuses sans index appropriés.

Solution:

  • Créez des index sur les colonnes groupBy.
  • Permet filter de limiter les lignes avant l’agrégation.
  • Utilisez having pour réduire le nombre de groupes retournés.

Étape suivante