GROUP BY を使用してデータを集計する

完了

集計関数は分析に役立ちますが、集計する前にデータをサブセットに配置することが必要な場合があります。 このセクションでは、GROUP BY 句を使用してこれを行う方法について説明します。

GROUP BY 句の使用

学習したように、SELECT ステートメントが処理されると、FROM 句と WHERE 句が評価された後に、仮想テーブルが作成されます。 仮想テーブルの内容を追加処理できるようになりました。 GROUP BY 句を使用すると、この仮想テーブルの内容を行のグループに分割できます。

行をグループ化するには、GROUP BY 句で 1 つ以上の要素を指定します。

GROUP BY <value1> [, <value2>, …]

GROUP BY は、句で指定された要素によって決定されたグループを作成し、行を各グループに配置します。

たとえば、次のクエリでは、Sales.SalesOrderHeader テーブル内の CustomerID ごとに 1 行ずつ、グループ化された行のセットが生成されます。 GROUP BY プロセスを見るもう 1 つの方法は、 CustomerID に同じ値を持つすべての行がグループ化され、1 つの結果行に返されることです。

SELECT CustomerID
FROM Sales.SalesOrderHeader
GROUP BY CustomerID;

上記のクエリは、次のクエリと同じです。

SELECT DISTINCT CustomerID
FROM Sales.SalesOrderHeader

GROUP BY 句が処理され、各行がグループに関連付けられた後、クエリの後のフェーズでは、SELECT リストに含まれているが GROUP BY リストには表示されないソース行の要素を集計する必要があります。 この要件は、SELECT 句と HAVING 句の記述方法に影響します。

では、GROUP BY または DISTINCT を使用してクエリを記述する場合の違いは何ですか? CustomerID の個別の値を知りたい場合は、違いはありません。 ただし、GROUP BY では、SELECT リストに他の要素を追加し、グループごとに集計することができます。

最も単純な集計関数は COUNT(*) です。 次のクエリでは、 CustomerID から元の 830 行のソース行を受け取り、 CustomerID 値に基づいて 89 グループにグループ化します。 個別の CustomerID 値ごとに、GROUP BY クエリで 1 行の出力が生成されます

SELECT CustomerID, COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY CustomerID;

各 CustomerID 値について、クエリは行を集計してカウントするため、SalesOrderHeader テーブル内の各顧客に属する行の数が表示されます。

顧客ID

注文数

1234

3

1005

1

GROUP BY では結果の順序が保証されないことに注意してください。 多くの場合、クエリ プロセッサによってグループ化操作が実行される方法の結果として、結果はグループ値の順序で返されます。 ただし、この動作に依存しないでください。 結果を並べ替える必要がある場合は、ORDER 句を明示的に含める必要があります。

SELECT CustomerID, COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY CustomerID
ORDER BY CustomerID;

今回は、指定した順序で結果が返されます。

顧客ID

注文数

1005

1

1234

3

SELECT ステートメントの句は、次の順序で適用されます。

  1. FROM
  2. どこ
  3. GROUP BY
  4. HAVING
  5. SELECT
  6. ORDER BY

列の別名は SELECT 句で割り当てられます。これは GROUP BY 句の 、ORDER BY 句の 前に 発生します。 列の別名は ORDER BY 句で参照できますが、GROUP BY 句では参照できません。 次のクエリでは、無効な 列名 エラーが発生します。

SELECT CustomerID AS Customer,
       COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY Customer
ORDER BY Customer;

ただし、次のクエリは成功し、顧客 ID で結果をグループ化して並べ替えます。

SELECT CustomerID AS Customer,
       COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY CustomerID
ORDER BY Customer;

GROUP BY エラーのトラブルシューティング

SELECT ステートメントで GROUP BY を使用することに慣れる一般的な障害は、次の種類のエラー メッセージが発生する理由を理解することです。

メッセージ 8120、レベル 16、状態 1、2 行目の列 <column_name> は、集計関数または GROUP BY 句に含まれていないため、選択リストでは無効です。

たとえば、SELECT リストの各列が GROUP BY 句の列であるか、各グループで動作する集計関数であるため、次のクエリが許可されます。

SELECT CustomerID, COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY CustomerID;

PurchaseOrderNumber は GROUP BY の一部ではなく、集計関数では使用されないため、次のクエリではエラーが返されます。

SELECT CustomerID, PurchaseOrderNumber, COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY CustomerID;

このクエリは、次のエラーを返します。

Msg 8120, Level 16, State 1, Line 1
Column 'Sales.SalesOrderHeader.PurchaseOrderNumber' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

もう 1 つの考え方を次に示します。 このクエリは、 CustomerID 値ごとに 1 行を返します。 ただし、同じ CustomerID の行には異なる PurchaseOrderNumber 値を指定できるため、返される値はどれですか?

顧客 ID および発注書ごとの注文を表示する場合は、次のように、GROUP BY 句に PurchaseOrderNumber 列を追加できます。

SELECT CustomerID, PurchaseOrderNumber, COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader
GROUP BY CustomerID, PurchaseOrderNumber;

このクエリでは、顧客ごとに 1 行、各発注書の組み合わせ、およびその組み合わせの注文数が返されます。