Condividi tramite


Aggregare i dati con GraphQL in Generatore di API Data

Il generatore di API dati supporta l'aggregazione GraphQL e il raggruppamento per i database della famiglia SQL e Azure Synapse Analytics (pool SQL dedicato). Le aggregazioni consentono di riepilogare i campi numerici e i risultati del gruppo senza scrivere codice API personalizzato. Aggregazione e groupBy non sono disponibili per Azure Cosmos DB per NoSQL, PostgreSQL o MySQL.

Prerequisiti

  • Database supportati:
    • SQL Server 2016 o versione successiva
    • Database SQL di Microsoft Azure
    • Istanza SQL gestita di Azure
    • Microsoft Fabric SQL
    • Azure Synapse Analytics (solo pool SQL dedicato)
  • CLI generatore di API dati. Installare l'interfaccia della riga di comando
  • Un file di configurazione DAB con l'entità esposta tramite GraphQL.
  • Un client GraphQL ,ad esempio Banana Cake Pop o GraphQL Playground, per eseguire query.

Database supportati

Banca dati Supporto delle aggregazioni
SQL Server/Azure SQL/Microsoft Fabric SQL ✅ Sì
Azure Synapse (pool SQL dedicato) ✅ Sì
Azure Synapse (pool SQL senza server) ❌ No
PostgreSQL ❌ No
MySQL ❌ No
Azure Cosmos DB per il NoSQL ❌ No

Funzioni di aggregazione

DAB supporta le funzioni di aggregazione seguenti:

Funzione Si applica a: Description
sum Solo campi numerici Totale di tutti i valori
average Solo campi numerici Media di tutti i valori
min Solo campi numerici Valore minimo
max Solo campi numerici Valore massimo
count Qualsiasi campo Numero di valori non Null

Constraints

  • sum, average, mine max funzionano solo sui tipi di dati numerici (int, decimal, float e così via).
  • count funziona su qualsiasi tipo di dati, incluse stringhe e date.
  • Se una tabella non contiene colonne numeriche, DAB non genera nodi di aggregazione per tale entità. È comunque possibile usare count in campi non numerici.

Modificatori facoltativi

Modificatore Scopo Example
distinct: true Conteggia solo valori univoci Contare i clienti distinti
having: { ... } Filtrare i gruppi dopo l'aggregazione Mostra gruppi con somma > 1000

Avviare il runtime DAB

Avviare DAB con il file di configurazione in modo che sia disponibile l'endpoint GraphQL.

dab start

Interrogare i risultati aggregati

Questa sezione illustra un esempio completo che illustra lo schema della tabella, la query GraphQL, la risposta SQL generata e JSON.

Schema tabella

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

Query GraphQL

Usare GraphQL per raggruppare le righe e restituire valori aggregati per i campi numerici.

{
  books(
    groupBy: { fields: ["year"] }
  ) {
    items {
      year
    }
    aggregates {
      pages {
        sum
        average
        min
        max
      }
    }
  }
}
  • groupBy.fields raggruppa le righe in base alle colonne specificate.
  • aggregates espone le funzioni di aggregazione per i campi numerici , ad esempio pages.
  • Lo schema GraphQL espone solo le aggregazioni per i campi che li supportano; usare l'introspezione dello schema nel client per confermare i campi e le funzioni aggregati disponibili.

SQL generato

DAB converte la query GraphQL in 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

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

Le items matrici e aggregates sono allineate in base all'indice: il primo elemento in aggregates.pages corrisponde al primo gruppo in items.

Aggregazione senza raggruppamento

Calcolare le aggregazioni in tutte le righe quando si omette groupBy.

Query GraphQL

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

SQL generato

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

Risposta JSON

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

Senza groupBy, la risposta restituisce un singolo oggetto (non una matrice) perché tutte le righe vengono compresse in un unico risultato.

Raggruppare uno o più campi

Raggruppare le righe in base a una o più colonne e restituire aggregazioni per gruppo.

Schema tabella

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

Query GraphQL

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

SQL generato

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

Risposta 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 risposta restituisce matrici per items e aggregazioni nello stesso ordine, in modo da poter allineare i gruppi ai valori aggregati.

Essere obbligati a filtrare i risultati aggregati

Usare having per filtrare i gruppi dopo l'aggregazione. Equivale alla clausola di HAVING SQL.

Schema tabella

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

Query GraphQL

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

SQL generato

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

Risposta JSON

Vengono restituite solo le categorie in cui la somma supera 10000:

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

Operatori HAVING

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

Annotazioni

Ogni having filtro si applica in modo indipendente alla funzione di aggregazione. Non è possibile creare condizioni di aggregazione incrociata come "sum > 1000 OR count < 10" in una singola query GraphQL.

DISTINCT nelle aggregazioni

Contare i valori univoci con distinct: true.

Schema tabella

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

Query GraphQL

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

SQL generato

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

Risposta JSON

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

Il primo count (con distinct: true) restituisce prodotti univoci per cliente. Il secondo count restituisce gli ordini totali.

Annotazioni

Quando si richiedono più aggregazioni nello stesso campo, DAB le restituisce nell'ordine richiesto. Usare alias (ad esempio, uniqueProducts: count(distinct: true)) per rendere le risposte auto-documentanti.

Combinare i filtri con l'aggregazione

Applicare filter alle righe prima del raggruppamento e having ai gruppi dopo l'aggregazione. Comprendere l'ordine delle operazioni è fondamentale:

  1. Filtro (SQL WHERE) rimuove le righe prima del raggruppamento
  2. Raggruppa per raccoglie le righe rimanenti in gruppi
  3. Aggrega calcola somma/media/min/max/conteggio per gruppo
  4. Having rimuove i gruppi che non soddisfano la condizione

Query GraphQL

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

SQL generato

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

Suggerimento

Usare filter per escludere righe prima dell'aggregazione. Usare having per filtrare i gruppi dopo l'aggregazione.

Usare gli alias con le aggregazioni

Creare nomi di campo significativi usando gli alias GraphQL.

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

Introspezione dello schema

Usare l'introspezione per vedere quali aggregazioni sono disponibili per un'entità.

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

I campi numerici espongono sum, averagemin, max, e count. I campi non numerici espongono count.

Suggerimenti e limitazioni

  • Aggregazione e groupBy si applicano solo a SQL Server, Azure SQL, Microsoft Fabric SQL e Azure Synapse Dedicated SQL pool.
  • Le aggregazioni vengono eseguite su campi numerici; count funziona su qualsiasi campo. Le tabelle senza colonne numeriche mostrano solo count.
  • Il raggruppamento si applica ai campi della stessa entità (nessun gruppo tra entitàBy).
  • Le aggregazioni di grandi dimensioni possono essere costose; indicizzare le colonne groupBy e filtrare le righe prima di raggrupparle, quando possibile.
  • Creare indici sulle colonne groupBy utilizzate frequentemente per migliorare le prestazioni delle query.

Risoluzione dei problemi

Errore: Il campo non supporta l'aggregazione

Causa: uso di sum, averagemin, o max in un campo non numerico.

Soluzione:

  • Usare l'introspezione dello schema per verificare i tipi di campo.
  • Utilizzare count per i campi non numerici.
  • Controlla i mapping dei campi se usi nomi di campi personalizzati.

Errore: Nodi di aggregazione non trovati

Causa: l'entità non ha colonne numeriche.

Soluzione:

  • Verificare che lo schema della tabella contenga almeno una colonna numerica.
  • Usare count le aggregazioni nei campi non numerici, se necessario.

Query di aggregazione lente

Causa: tabelle di grandi dimensioni senza indici appropriati.

Soluzione:

  • Creare indici su colonne groupBy.
  • Usare filter per limitare le righe prima dell'aggregazione.
  • Usare having per ridurre il numero di gruppi restituiti.

Passo successivo