Agregowanie danych za pomocą języka GraphQL

Konstruktor Data API obsługuje agregacje GraphQL dla encji z rodziny SQL Server oraz dedykowanej puli SQL w usłudze Azure Synapse Analytics. Użyj pola groupBy w zapytaniu do kolekcji, aby obliczyć wartości sum, avg, min, max i count.

Przykłady w tym artykule używają jednostek SQL Server i GraphQL z uprawnieniami do odczytu. Konstruktor interfejsu API danych generuje język SQL, podobnie jak instrukcje wyświetlane w każdym scenariuszu. Wartości parametrów są wyświetlane jako parametry zapytania w czasie wykonywania. Funkcje sum, avg, mini max mają zastosowanie do pól liczbowych. Funkcja count działa w dowolnym polu.

Important

Agregacja nie jest dostępna dla Azure Cosmos DB dla NoSQL, PostgreSQL lub MySQL.

Agregacja jest domyślnie włączona. Aby to wyłączyć, ustaw enable-aggregation na false w sekcji runtime.graphql w pliku konfiguracyjnym. Zapytania agregacji zwracają jedną stronę grup, domyślnie 100. Użyj argumentu first w zapytaniu kolekcji, aby zmienić wartość maksymalną, na przykład books(first: 500). Ustaw wartość domyślną na runtime.pagination.default-page-size.

Dodatki schematu GraphQL

Po włączeniu agregacji konstruktor interfejsu API danych dodaje pola agregacji i generowane typy do każdej obsługiwanej kolekcji GraphQL. Dokładne wygenerowane nazwy typów są specyficzne dla jednostki i widoczne za pośrednictwem introspekcji GraphQL, ale składnia zapytania jest spójna między jednostkami.

Poniższe fragmenty kodu pokazują formaty składni, a nie kompletne zapytania GraphQL.

groupBy

Zwraca wiersze pogrupowane w kolekcji. Wybierz ten składnik zamiast items w zapytaniu agregującym.

<collection> { groupBy { ... } }

groupBy(fields: [...])

Określa pola encji, według których ma być wykonywane grupowanie. Pomiń fields, aby zagregować wszystkie wiersze w jedną grupę.

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

fields

Zwraca pogrupowane wartości pól dla każdego wiersza w wyniku agregacji.

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

aggregations

Zawiera opcje funkcji agregującej dla każdej grupy.

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

sum, avg, mini max

Agregowanie pól liczbowych.

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

Zlicza wartości dla pola.

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

field

Identyfikuje pole jednostki do agregowania. Nazwy pól są wartościami typu wyliczeniowego, a nie łańcuchami znaków.

<function>(field: <field>)

having

Filtruje grupy po obliczeniu wartości zagregowanej przez kreator interfejsu API danych.

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

distinct

Zlicza unikalne wartości, gdy jest używane z count.

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

Konstruktor Data API generuje dla tych składowych typy GraphQL specyficzne dla encji, w tym typy wierszy grup, typy pól grupowanych, typy wyboru agregatów, wyliczenia pól oraz typy wejściowe having. Nie musisz nazywać tych wygenerowanych typów w zapytaniu.

Samples

Poniższe przykłady pokazują tabelę SQL, zapytanie GraphQL, wynikowe zapytanie SQL oraz wynik dla typowych wzorców agregacji.

Agregowanie wszystkich wierszy w tabeli

Użyj tego wzorca, jeśli chcesz utworzyć jeden wiersz podsumowania dla całej jednostki.

Tabela 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 rok Stron
1 GraphQL Basics 2023 120
2 Zaawansowane interfejsy API 2023 450
3 Wzorce danych 2023 390
4 Interfejsy API w chmurze 2024 140
5 Wewnętrzne środowiska uruchomieniowego 2024 510
6 Dostrajanie zapytań 2024 250

Zapytanie GraphQL

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

Wynikowy kod 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;

Wynikowe dane wyjściowe

{
  "data": {
    "books": {
      "groupBy": [
        {
          "aggregations": {
            "totalPages": 1860,
            "averagePages": 310,
            "shortestBook": 120,
            "longestBook": 510,
            "bookCount": 6
          }
        }
      ]
    }
  }
}
totalPages średnia liczba stron najkrótsza książka najdłuższaKsiążka bookCount
1860 310 120 510 6

Grupowanie wierszy według jednego pola

Użyj groupBy(fields: [...]), aby zwrócić jeden wiersz zagregowany dla każdej wartości pola. Nazwy pól są wartościami wyliczeniowymi GraphQL, a nie łańcuchami znaków.

Tabela 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 rok Stron
1 GraphQL Basics 2023 120
2 Zaawansowane interfejsy API 2023 450
3 Wzorce danych 2023 390
4 Interfejsy API w chmurze 2024 140
5 Wewnętrzne środowiska uruchomieniowego 2024 510
6 Dostrajanie zapytań 2024 250

Zapytanie GraphQL

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

Wynikowy kod 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;

Wynikowe dane wyjściowe

{
  "data": {
    "books": {
      "groupBy": [
        {
          "fields": {
            "year": 2023
          },
          "aggregations": {
            "totalPages": 960,
            "averagePages": 320
          }
        },
        {
          "fields": {
            "year": 2024
          },
          "aggregations": {
            "totalPages": 900,
            "averagePages": 300
          }
        }
      ]
    }
  }
}
rok totalPages średnia liczba stron
2023 960 320
2024 900 300

Grupuj wiersze w widoku

Agregacja działa również w przypadku jednostek opartych na widoku. Skonfiguruj pole klucza dla widoku, aby konstruktor interfejsu API danych mógł uwidocznić je jako jednostkę.

widok 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 departament wiek
1 Inżynieria 29
2 Inżynieria 41
3 Sales 34
4 Sales 52
5 Support 25
6 Support 38
7 Inżynieria 45

Skonfiguruj widok z polem id jako polem kluczowym:

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

Zapytanie GraphQL

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

Wynikowy kod 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;

Wynikowe dane wyjściowe

{
  "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
          }
        }
      ]
    }
  }
}
departament najmłodszy najstarszy Liczba pracowników
Inżynieria 29 45 3
Sales 34 52 2
Support 25 38 2

Filtrowanie wierszy przed agregacją

Użyj filter w zapytaniu kolekcji, aby ograniczyć liczbę wierszy źródłowych, zanim Data API builder je pogrupuje i zagreguje.

widok 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 departament wiek
1 Inżynieria 29
2 Inżynieria 41
3 Sales 34
4 Sales 52
5 Support 25
6 Support 38
7 Inżynieria 45

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

Wynikowy kod 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;

W przypadku tego zapytania @param1 wartość to 30.

Wynikowe dane wyjściowe

{
  "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
          }
        }
      ]
    }
  }
}
departament najmłodszy najstarsze Liczba pracowników
Inżynieria 41 45 2
Sales 34 52 2
Support 38 38 1

Filtrowanie grup przy użyciu polecenia having

Użyj having funkcji agregującej, aby filtrować grupy po agregacji. Ten wzorzec mapuje na klauzulę SQL HAVING .

Tabela 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 kategoria cena
1 Elektronika 5000.00
2 Elektronika 10000.00
3 Meble 4000.00
4 Meble 8000.00
5 Książki 100,00
6 Książki 200,00

Zapytanie GraphQL

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

Wynikowy kod 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;

W przypadku tego zapytania @param1 to 10000.

Wynikowe dane wyjściowe

{
  "data": {
    "products": {
      "groupBy": [
        {
          "fields": {
            "category": "Electronics"
          },
          "aggregations": {
            "totalValue": 15000,
            "averagePrice": 7500
          }
        },
        {
          "fields": {
            "category": "Furniture"
          },
          "aggregations": {
            "totalValue": 12000,
            "averagePrice": 6000
          }
        }
      ]
    }
  }
}
kategoria łączna wartość średnia cena
Elektronika 15000 7500
Meble 12000 6000

Zlicz unikatowe wartości

Użyj polecenia distinct: true , count aby zliczyć unikatowe wartości w każdej grupie.

Tabela 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

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

Wynikowy kod 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;

Wynikowe dane wyjściowe

{
  "data": {
    "orders": {
      "groupBy": [
        {
          "fields": {
            "customer_id": 101
          },
          "aggregations": {
            "uniqueProducts": 5,
            "totalOrders": 6
          }
        },
        {
          "fields": {
            "customer_id": 102
          },
          "aggregations": {
            "uniqueProducts": 3,
            "totalOrders": 4
          }
        }
      ]
    }
  }
}
customer_id uniqueProducts Łączna liczba zamówień
101 5 6
102 3 4

Typowe błędy

  • Wybierz groupBy wewnątrz pola kolekcji. Nie przekazuj groupBy jako argument kolekcji.
  • Użyj aggregations, a nie aggregates.
  • Użyj avg, a nie average.
  • Użyj wartości wyliczeniowych pola, takich jak fields: [year]. Nie cytuj nazw pól.
  • Nie wybieraj items i groupBy w tym samym zapytaniu do kolekcji.
  • Po zgrupowaniu według pól zaznacz tylko te same pola wewnątrz fields obiektu.