次の方法で共有


Data API ビルダーで GraphQL を使用してデータを集計する

データ API ビルダー (DAB) では、SQL ファミリ データベースと Azure Synapse Analytics (専用 SQL プール) の GraphQL 集計とグループ化がサポートされています。 集計を使用すると、カスタム API コードを記述せずに数値フィールドを集計し、結果をグループ化できます。 集計と groupBy は、Azure Cosmos DB for NoSQL、PostgreSQL、または MySQL では使用できません。

[前提条件]

  • サポートされているデータベース:
    • SQL Server 2016 以降
    • Azure SQL Database
    • Azure SQL Managed Instance
    • Microsoft Fabric SQL
    • Azure Synapse Analytics (専用 SQL プールのみ)
  • データ API ビルダー CLI。 CLI をインストールする
  • GraphQLを通してエンティティが公開されているDAB構成ファイル。
  • クエリを実行する GraphQL クライアント (バナナ ケーキ ポップや GraphQL プレイグラウンドなど)。

サポートされるデータベース

データベース 集計のサポート
SQL Server / Azure SQL / Microsoft Fabric SQL ✅ はい
Azure Synapse (専用 SQL プール) ✅ はい
Azure Synapse (サーバーレス SQL プール) ❌ いいえ
PostgreSQL ❌ いいえ
MySQL ❌ いいえ
Azure Cosmos DB for NoSQL ❌ いいえ

集計関数

DAB では、次の集計関数がサポートされています。

機能 対象 Description
sum 数値フィールドのみ すべての値の合計
average 数値フィールドのみ すべての値の平均
min 数値フィールドのみ 最小値
max 数値フィールドのみ 最大値
count 任意のフィールド null 以外の値の数

Constraints

  • sumaveragemin、および max は、数値データ型 (int、decimal、float など) でのみ機能します。
  • count は、文字列や日付を含む任意のデータ型で機能します。
  • テーブルに数値列がない場合、DAB はそのエンティティの集計ノードを生成しません。 数値以外のフィールドでも count を使用できます。

省略可能な修飾子

修飾子 目的 Example
distinct: true 一意の値のみをカウントする 個別の顧客をカウントする
having: { ... } 集計後にグループをフィルター処理する 合計が 1,000 > グループを表示する

DAB ランタイムを実行する

GraphQL エンドポイントを使用できるように、構成ファイルで DAB を起動します。

dab start

集計結果のクエリ

このセクションでは、テーブル スキーマ、GraphQL クエリ、生成された SQL、JSON 応答を示す完全な例について説明します。

テーブル スキーマ

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

GraphQL クエリ

GraphQL を使用して行をグループ化し、数値フィールドの集計値を返します。

{
  books(
    groupBy: { fields: ["year"] }
  ) {
    items {
      year
    }
    aggregates {
      pages {
        sum
        average
        min
        max
      }
    }
  }
}
  • groupBy.fields は、指定した列ごとに行をグループ化します。
  • aggregates では、数値フィールドの集計関数 (たとえば、 pages) が公開されます。
  • GraphQL スキーマでは、それらをサポートするフィールドの集計のみが公開されます。クライアントでスキーマのイントロスペクションを使用して、使用可能な集計フィールドと関数を確認します。

生成された SQL

DAB は GraphQL クエリを 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

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

items配列とaggregates配列はインデックスで整列されます。aggregates.pagesの最初の要素は、itemsの最初のグループに対応します。

グループ化を使用せずに集計する

groupByを省略すると、すべての行の集計が計算されます。

GraphQL クエリ

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

生成された SQL

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

JSON 応答

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

groupByしないと、すべての行が 1 つの結果に折りたたまれるため、応答は (配列ではなく) 1 つのオブジェクトを返します。

1 つ以上のフィールドでグループ化する

行を 1 つ以上の列でグループ化し、グループごとに集計を返します。

テーブル スキーマ

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

GraphQL クエリ

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

生成された SQL

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

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

応答は、 items と集計の配列を同じ順序で返すため、グループをそれらの集計値に合わせて調整できます。

集計された結果をフィルター処理する

集計後にグループをフィルター処理するには、 having を使用します。 これは、SQL の HAVING 句と同じです。

テーブル スキーマ

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

GraphQL クエリ

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

生成された SQL

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

JSON 応答

合計が 10000 を超えるカテゴリのみが返されます。

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

HAVING 演算子

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

having フィルターは、集計関数に個別に適用されます。 "sum > 1000 OR count < 10" のようなクロス集計条件を 1 つの GraphQL クエリで作成することはできません。

集計における DISTINCT

distinct: trueを使用して一意の値をカウントします。

テーブル スキーマ

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

GraphQL クエリ

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

生成された SQL

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

JSON 応答

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

最初の count ( distinct: true) は、顧客ごとに一意の製品を返します。 2 番目の count は、合計注文数を返します。

同じフィールドに対して複数の集計を要求すると、DAB は要求された順序で集計を返します。 エイリアス (たとえば、 uniqueProducts: count(distinct: true)) を使用して、応答を自己文書化します。

フィルターと集計を組み合わせる

グループ化する前に行に filter を適用し、集計後にグループに having します。 操作の順序を理解することは重要です。

  1. フィルター (SQL WHERE) は、グループ化する前に行を削除します
  2. グループ化 残りの行をグループにまとめる
  3. 集計 では、グループあたりの合計/avg/min/max/count が計算されます
  4. `Having は 条件に一致しないグループを削除する`

GraphQL クエリ

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

生成された SQL

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

ヒント

集計の前に行を除外するには、 filter を使用します。 集計後にグループをフィルター処理するには、 having を使用します。

集計でエイリアスを使用する

GraphQL エイリアスを使用してわかりやすいフィールド名を作成します。

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

スキーマのイントロスペクション

イントロスペクションを使用して、エンティティにどの集計が利用可能かを見てみましょう。

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

数値フィールドは、 sumaverageminmax、および countを公開します。 数値以外のフィールドは countを公開します。

ヒントと制限事項

  • 集計と groupBy は、SQL Server、Azure SQL、Microsoft Fabric SQL、Azure Synapse 専用 SQL プールにのみ適用されます。
  • 集計は数値フィールドで実行されます。 count は任意のフィールドで動作します。 数値列のないテーブルは、 countのみを公開します。
  • グループ化は、同じエンティティのフィールドに適用されます (エンティティ間の groupBy はありません)。
  • 大規模な集計にはコストがかかる場合があります。可能であれば、グループ化する前に groupBy 列のインデックスを作成し、行をフィルター処理します。
  • クエリのパフォーマンスを向上させるために、頻繁に使用される groupBy 列にインデックスを作成します。

トラブルシューティング

エラー: フィールドは集計をサポートしていません

原因: 数値以外のフィールドで sumaveragemin、または max を使用する。

解決策:

  • スキーマのイントロスペクションを使用して、フィールド型を確認します。
  • 数値以外のフィールドには count を使用します。
  • ユーザー設定フィールド名を使用している場合は、フィールド マッピングを確認します。

エラー: 集計ノードが見つかりません

原因: エンティティに数値列がありません。

解決策:

  • テーブル スキーマに少なくとも 1 つの数値列があることを確認します。
  • 必要に応じて、数値以外のフィールドで count 集計を使用します。

低速集計クエリ

原因: 適切なインデックスのない大きなテーブル。

解決策:

  • groupBy列にインデックスを作成します。
  • 集計前に行を制限するには、 filter を使用します。
  • havingを使用して、返されるグループの数を減らします。

次のステップ