Aggregieren von Daten mit GraphQL

Data API builder unterstützt die GraphQL-Aggregation für Entitäten der SQL-Server-Produktfamilie und von dedizierten SQL-Pools in Azure Synapse Analytics. Verwenden Sie das Feld groupBy in einer Sammlungsabfrage, um sum-, avg-, min-, max- und count-Werte zu berechnen.

In den Beispielen in diesem Artikel werden SQL Server- und GraphQL-Entitäten mit Leseberechtigung verwendet. Der Data API Builder generiert SQL-Anweisungen, wie sie in den jeweiligen Szenarien dargestellt sind. Parameterwerte werden zur Laufzeit als Abfrageparameter angezeigt. Die sum, avg, minund max Funktionen gelten für numerische Felder. Die count Funktion funktioniert für jedes Feld.

Important

Aggregation ist für Azure Cosmos DB für NoSQL, PostgreSQL oder MySQL nicht verfügbar.

Die Aggregation ist standardmäßig aktiviert. Um es zu deaktivieren, setzen Sie enable-aggregation unter runtime.graphql in Ihrer Konfigurationsdatei auf false. Aggregationsabfragen geben eine Seite mit Gruppen zurück, standardmäßig 100. Verwenden Sie das first Argument für die Auflistungsabfrage, um das Maximum zu ändern, z. B books(first: 500). . Legen Sie dies mit runtime.pagination.default-page-size als Standard fest.

Erweiterungen am GraphQL-Schema

Wenn Sie aggregation aktivieren, fügt der Daten-API-Generator Aggregationsfelder und generierte Typen zu jeder unterstützten GraphQL-Auflistung hinzu. Die genau generierten Typnamen sind entitätsspezifisch und über die GraphQL-Introspection sichtbar, die Abfragesyntax ist jedoch für alle Entitäten konsistent.

Die folgenden Codeausschnitte zeigen Syntaxformate, keine vollständigen GraphQL-Abfragen.

groupBy

Gibt gruppierte Zeilen für die Sammlung zurück. Wählen Sie dieses Mitglied anstelle von items in einer Aggregationsabfrage aus.

<collection> { groupBy { ... } }

groupBy(fields: [...])

Listet die Entitätsfelder auf, nach denen gruppiert werden soll. fields weglassen, um alle Zeilen zu einer Gruppe zusammenzufassen.

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

fields

Gibt die gruppierten Feldwerte für jede Zeile im Aggregationsergebnis zurück.

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

aggregations

Enthält die Auswahl der Aggregatfunktion für jede Gruppe.

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

sum, avg, min und max

Aggregieren numerischer Felder.

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

Zählt Werte für ein Feld.

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

field

Identifiziert das zu aggregierende Entitätsfeld. Feldnamen sind Enumerationswerte und keine Zeichenfolgen.

<function>(field: <field>)

having

Filtert Gruppen, nachdem der Daten-API-Generator den Aggregatwert berechnet.

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

distinct

Zählt eindeutige Werte bei Verwendung mit count.

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

Der Daten-API-Generator generiert entitätsspezifische GraphQL-Typen hinter diesen Mitgliedern, einschließlich Gruppenzeilentypen, gruppierte Feldtypen, aggregierte Auswahltypen, Feldenumen und having Eingabetypen. Sie müssen diese generierten Typen in einer Abfrage nicht benennen.

Samples

Die folgenden Beispiele zeigen die SQL-Tabelle, die GraphQL-Abfrage, die resultierende SQL-Abfrage und die resultierende Ausgabe für allgemeine Aggregationsmuster.

Aggregieren aller Zeilen in einer Tabelle

Verwenden Sie dieses Muster, wenn Sie eine Hauptzeile für die gesamte Entität benötigen.

SQL-Tabelle

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 Jahr Seiten
1 GraphQL-Grundlagen 2023 120
2 Erweiterte APIs 2023 450
3 Datenmuster 2023 390
4 Cloud-APIs 2024 140
5 Laufzeitinterna 2024 510
6 Abfrageoptimierung 2024 250

GraphQL-Abfrage

{
  books {
    groupBy {
      aggregations {
        totalPages: sum(field: pages)
        averagePages: avg(field: pages)
        shortestBook: min(field: pages)
        longestBook: max(field: pages)
        bookCount: count(field: id)
      }
    }
  }
}

Resultierendes SQL

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;

Ergebnisausgabe

{
  "data": {
    "books": {
      "groupBy": [
        {
          "aggregations": {
            "totalPages": 1860,
            "averagePages": 310,
            "shortestBook": 120,
            "longestBook": 510,
            "bookCount": 6
          }
        }
      ]
    }
  }
}
totalPages Durchschnittliche Seiten shortestBook longestBook AnzahlBücher
1,860 310 120 510 6

Zeilen nach einem Feld gruppieren

Verwenden Sie groupBy(fields: [...]), um pro Feldwert eine Aggregatzeile zurückzugeben. Feldnamen sind GraphQL-Enumerationswerte und keine Zeichenfolgen.

SQL-Tabelle

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 Jahr Seiten
1 GraphQL-Grundlagen 2023 120
2 Erweiterte APIs 2023 450
3 Datenmuster 2023 390
4 Cloud-APIs 2024 140
5 Laufzeitinterna 2024 510
6 Abfrageoptimierung 2024 250

GraphQL-Abfrage

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

Resultierendes SQL

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;

Ergebnisausgabe

{
  "data": {
    "books": {
      "groupBy": [
        {
          "fields": {
            "year": 2023
          },
          "aggregations": {
            "totalPages": 960,
            "averagePages": 320
          }
        },
        {
          "fields": {
            "year": 2024
          },
          "aggregations": {
            "totalPages": 900,
            "averagePages": 300
          }
        }
      ]
    }
  }
}
Jahr totalPages averagePages
2023 960 320
2024 900 300

Zeilen aus einer Ansicht gruppieren

Die Aggregation funktioniert auch für durch Ansichten gestützte Entitäten. Konfigurieren Sie ein Schlüsselfeld für die Ansicht, damit der Daten-API-Generator es als Entität verfügbar machen kann.

SQL-Ansicht

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 department Alter
1 Ingenieurwesen 29
2 Ingenieurwesen 41
3 Sales 34
4 Sales 52
5 Support 25
6 Support 38
7 Ingenieurwesen 45

Konfigurieren Sie die Ansicht mit id als Schlüsselfeld:

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

GraphQL-Abfrage

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

Resultierendes SQL

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;

Ergebnisausgabe

{
  "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
          }
        }
      ]
    }
  }
}
department Jüngste älteste employeeCount
Ingenieurwesen 29 45 3
Sales 34 52 2
Support 25 38 2

Filtern von Zeilen vor Aggregation

Verwenden Sie filter in der Sammlungsabfrage, um Quellzeilen zu begrenzen, bevor Data API builder sie gruppiert und aggregiert.

SQL-Ansicht

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 department Alter
1 Ingenieurwesen 29
2 Ingenieurwesen 41
3 Sales 34
4 Sales 52
5 Support 25
6 Support 38
7 Ingenieurwesen 45

GraphQL-Abfrage

{
  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)
      }
    }
  }
}

Resultierendes SQL

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;

Für diese Abfrage @param1 lautet 30.

Ergebnisausgabe

{
  "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
          }
        }
      ]
    }
  }
}
department Jüngste älteste Mitarbeiterzahl
Ingenieurwesen 41 45 2
Sales 34 52 2
Support 38 38 1

Filtern von Gruppen mithilfe von having

Verwenden Sie having, um Gruppen nach der Aggregation bei einer Aggregatfunktion zu filtern. Dieses Muster ist einer SQL-Klausel HAVING zugeordnet.

SQL-Tabelle

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 Kategorie Preis
1 Elektronik 5000.00
2 Elektronik 10000,00
3 Möbel 4000.00
4 Möbel 8000.00
5 Bücher 100.00
6 Bücher 200.00

GraphQL-Abfrage

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

Resultierendes SQL

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;

Für diese Abfrage @param1 lautet 10000.

Ergebnisausgabe

{
  "data": {
    "products": {
      "groupBy": [
        {
          "fields": {
            "category": "Electronics"
          },
          "aggregations": {
            "totalValue": 15000,
            "averagePrice": 7500
          }
        },
        {
          "fields": {
            "category": "Furniture"
          },
          "aggregations": {
            "totalValue": 12000,
            "averagePrice": 6000
          }
        }
      ]
    }
  }
}
Kategorie totalValue Durchschnittspreis
Elektronik 15000 7.500
Möbel 12000 6000

Zählen unterschiedlicher Werte

Verwenden Sie distinct: true mit count, um eindeutige Werte in jeder Gruppe zu zählen.

SQL-Tabelle

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 Kunden_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

GraphQL-Abfrage

{
  orders(orderBy: { customer_id: ASC }) {
    groupBy(fields: [customer_id]) {
      fields { customer_id }
      aggregations {
        uniqueProducts: count(field: product_id, distinct: true)
        totalOrders: count(field: id)
      }
    }
  }
}

Resultierendes SQL

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;

Ergebnisausgabe

{
  "data": {
    "orders": {
      "groupBy": [
        {
          "fields": {
            "customer_id": 101
          },
          "aggregations": {
            "uniqueProducts": 5,
            "totalOrders": 6
          }
        },
        {
          "fields": {
            "customer_id": 102
          },
          "aggregations": {
            "uniqueProducts": 3,
            "totalOrders": 4
          }
        }
      ]
    }
  }
}
Kunden_ID Eindeutige Produkte Gesamtbestellungen
101 5 6
102 3 4

Häufige Fehler

  • Wählen Sie groupBy innerhalb des Auflistungsfelds aus. Übergeben Sie groupBy nicht als Sammlungsargument.
  • Verwenden Sie aggregations, nicht aggregates.
  • Verwenden Sie avg, nicht average.
  • Verwenden Sie Enum-Werte von Feldern, z. B. fields: [year]. Geben Sie keine Anführungszeichen für Feldnamen an.
  • Wählen Sie items und groupBy nicht in derselben Sammlungsabfrage aus.
  • Wenn Sie nach Feldern gruppieren, markieren Sie nur die gleichen Felder innerhalb des fields Objekts.