FROM - PIVOT および UNPIVOT の使用
適用対象: SQL Server Azure SQL Database Azure SQL Managed Instance Azure Synapse Analytics Analytics Platform System (PDW) Microsoft Fabric の SQL 分析エンドポイント Microsoft Fabric Warehouse Microsoft Fabric SQL Database
関係演算子 PIVOT
および UNPIVOT
を使用すると、テーブル値式を別のテーブルに変更できます。 PIVOT
では、式内の 1 つの列にある複数の一意の値を出力内の複数の列に変えることにより、テーブル値式が行列変換されます。 PIVOT
また、最終的な出力で必要な残りの列値に必要な集計も実行されます。 UNPIVOT
では、テーブル値式の列を列値に回転することで、 PIVOT
とは逆の操作が実行されます。
PIVOT
の構文は、複雑な一連のSELECT...CASE
ステートメントで指定できる構文よりも簡単で読みやすいです。 PIVOT
の構文の詳細については、FROM 句を参照してください。
Note
1 つの T-SQL ステートメント内で PIVOT
/UNPIVOT
を繰り返し使用すると、クエリのパフォーマンスに悪影響を及ぼす可能性があります。
この記事の Transact-SQL コード サンプルは AdventureWorks2022
または AdventureWorksDW2022
サンプル データベースを使用します。このサンプル データベースは、Microsoft SQL Server サンプルとコミュニティ プロジェクトのホーム ページからダウンロードできます。
構文
このセクションでは、 PIVOT
演算子と UNPIVOT
演算子の使用方法について説明します。
PIVOT
演算子の構文。
SELECT [ <non-pivoted column> [ AS <column name> ] , ]
...
[ <first pivoted column> [ AS <column name> ] ,
[ <second pivoted column> [ AS <column name> ] , ]
...
[ <last pivoted column> [ AS <column name> ] ] ]
FROM
( <SELECT query that produces the data> )
AS <alias for the source query>
PIVOT
(
<aggregation function> ( <column being aggregated> )
FOR <column that contains the values that become column headers>
IN ( <first pivoted column>
, <second pivoted column>
, ... <last pivoted column> )
) AS <alias for the pivot table>
[ <optional ORDER BY clause> ]
[ ; ]
UNPIVOT
演算子の構文。
SELECT [ <non-pivoted column> [ AS <column name> ] , ]
...
[ <output column for names of the pivot columns> [ AS <column name> ] , ]
[ <new output column created for values in result of the source query> [ AS <column name> ] ]
FROM
( <SELECT query that produces the data> )
AS <alias for the source query>
UNPIVOT
(
<new output column created for values in result of the source query>
FOR <output column for names of the pivot columns>
IN ( <first pivoted column>
, <second pivoted column>
, ... <last pivoted column> )
)
[ <optional ORDER BY clause> ]
[ ; ]
解説
UNPIVOT
句内の列識別子は、カタログ照合順序に従います。
Azure SQL Database の場合、照合順序は常に
SQL_Latin1_General_CP1_CI_AS
。SQL Server の部分的包含データベースの場合、照合順序は常に
Latin1_General_100_CI_AS_KS_WS_SC
です。
列が他の列と結合されている場合、競合を回避するために COLLATE 句 (COLLATE DATABASE_DEFAULT
) が必要です。
Microsoft Fabric および Azure Synapse Analytics プールでは、PIVOT
演算子を使用したクエリは、PIVOT
によって非ピボット列の出力にGROUP BY
がある場合に失敗します。 回避策として、 GROUP BY
からピボットされていない列を削除します。 クエリ結果は同じです。この GROUP BY
句は重複しています。
基本的な PIVOT の例
次のコード例では、4 行、2 列で構成されるテーブルを生成します。
USE AdventureWorks2022;
GO
SELECT DaysToManufacture, AVG(StandardCost) AS AverageCost
FROM Production.Product
GROUP BY DaysToManufacture;
結果セットは次のとおりです。
DaysToManufacture AverageCost
------------------ ------------
0 5.0885
1 223.88
2 359.1082
4 949.4105
DaysToManufacture
の値が 3
の製品は定義されていません。
次のコードでは、同じ結果が、DaysToManufacture
値を列見出しにピボット処理して表示されます。 結果がNULL
されている場合でも、3 日間 ([3]
) 列が提供されます。
-- Pivot table with one row and five columns
SELECT 'AverageCost' AS CostSortedByProductionDays,
[0], [1], [2], [3], [4]
FROM (
SELECT DaysToManufacture,
StandardCost
FROM Production.Product
) AS SourceTable
PIVOT (
AVG(StandardCost) FOR DaysToManufacture IN
([0], [1], [2], [3], [4])
) AS PivotTable;
結果セットは次のとおりです。
CostSortedByProductionDays 0 1 2 3 4
--------------------------- ----------- ----------- ----------- ----------- -----------
AverageCost 5.0885 223.88 359.1082 NULL 949.4105
複雑な PIVOT の例
PIVOT
関係演算子が役立つ一般的なシナリオは、データの概要を示すためのクロス集計レポートを生成する場合です。 たとえば、AdventureWorks2022
サンプル データベースの PurchaseOrderHeader
テーブルにクエリを実行し、特定の従業員の発注数を抽出するとします。 このレポートを仕入先別に返すクエリを次に示します。
USE AdventureWorks2022;
GO
SELECT VendorID,
[250] AS Emp1,
[251] AS Emp2,
[256] AS Emp3,
[257] AS Emp4,
[260] AS Emp5
FROM
(
SELECT PurchaseOrderID,
EmployeeID, VendorID
FROM Purchasing.PurchaseOrderHeader
) p
PIVOT
(
COUNT (PurchaseOrderID)
FOR EmployeeID IN ([250], [251], [256], [257], [260])
) AS pvt
ORDER BY pvt.VendorID;
次に結果セットの一部を示します。
VendorID Emp1 Emp2 Emp3 Emp4 Emp5
----------- ----------- ----------- ----------- ----------- -----------
1492 2 5 4 4 4
1494 2 5 4 5 4
1496 2 4 4 5 5
1498 2 5 4 4 4
1500 3 4 4 5 4
このサブセレクト ステートメントにより返される結果は EmployeeID
列でピボット処理されています。
SELECT PurchaseOrderID,
EmployeeID,
VendorID
FROM PurchaseOrderHeader;
EmployeeID
列から返される一意の値が、最終的な結果セットのフィールドになります。 そのため、ピボット句で指定された各 EmployeeID
番号の列があります。この例では、従業員の 250
、 251
、 256
、 257
、および 260
です。 PurchaseOrderID
列は、最終的な出力に返される列をグループ化する (この列をグループ化列といいます) のための値列です。 この例では、グループ化列を COUNT
関数で集計しています。 各従業員のCOUNT
を計算するときに、PurchaseOrderID
列に表示される null 値が考慮されなかったことを示す警告メッセージが表示されます。
重要
集計関数を PIVOT
と共に使用する場合、集計を計算するときに値列に null 値が存在することは考慮されません。
UNPIVOT の例
UNPIVOT
関係演算子で行われる操作は、基本的に PIVOT
演算子の逆で、列を行に変換します。 上記の例で作成されたテーブルが pvt
という名前でデータベースに保存されていて、列 ID Emp1
、Emp2
、Emp3
、Emp4
、および Emp5
を、特定の仕入先に対応する行の値に行列変換するとします。 そのため、2 つの追加の列を識別する必要があります。
回転する列の値 (Emp1
、 Emp2
など) を含む列は Employee
と呼ばれ、回転中の列の下に現在存在する値を保持する列は Orders
と呼ばれます。 これらの列は、それぞれ、Transact-SQL 定義の pivot_column と value_column に対応します。 クエリは以下のとおりです。
-- Create the table and insert values as portrayed in the previous example.
CREATE TABLE pvt (
VendorID INT,
Emp1 INT,
Emp2 INT,
Emp3 INT,
Emp4 INT,
Emp5 INT);
GO
INSERT INTO pvt
VALUES (1, 4, 3, 5, 4, 4);
INSERT INTO pvt
VALUES (2, 4, 1, 5, 5, 5);
INSERT INTO pvt
VALUES (3, 4, 3, 5, 4, 4);
INSERT INTO pvt
VALUES (4, 4, 2, 5, 5, 4);
INSERT INTO pvt
VALUES (5, 5, 1, 5, 5, 5);
GO
-- Unpivot the table.
SELECT VendorID, Employee, Orders
FROM (
SELECT VendorID, Emp1, Emp2, Emp3, Emp4, Emp5
FROM pvt
) p
UNPIVOT
(
Orders FOR Employee IN (Emp1, Emp2, Emp3, Emp4, Emp5)
) AS unpvt;
GO
次に結果セットの一部を示します。
VendorID Employee Orders
----------- ----------- ------
1 Emp1 4
1 Emp2 3
1 Emp3 5
1 Emp4 4
1 Emp5 4
2 Emp1 4
2 Emp2 1
2 Emp3 5
2 Emp4 5
2 Emp5 5
UNPIVOT
は、 PIVOT
の正確な逆ではありません。 PIVOT
関係演算子を実行すると集計が行われ、複数である可能性のある行が出力では 1 つの行にマージされます。 UNPIVOT
では、行がマージされているため、元のテーブル値式の結果は再現されません。 また、UNPIVOT
入力のNULL
値が出力に表示されなくなります。 値が消えると、PIVOT
操作の前に入力に元のNULL
値があった可能性があることを示します。
AdventureWorks2022
サンプル データベースのビュー Sales.vSalesPersonSalesByFiscalYears
では、PIVOT
関係演算子を使用して会計年度別に販売員ごとの総売上を返します。 SQL Server Management Studio でビューをスクリプト化するには、オブジェクト エクスプローラーの AdventureWorks2022
データベースの [ビュー] フォルダーで、ビューを検索します。 ビュー名を右クリックし、 [ビューをスクリプト化] をクリックします。