T-SQLのPIVOT関数を理解する


83

私はSQLにとても慣れていません。

私はこのようなテーブルを持っています:

ID | TeamID | UserID | ElementID | PhaseID | Effort
-----------------------------------------------------
1  |   1    |  1      |   3       |  5     |   6.74
2  |   1    |  1      |   3       |  6     |   8.25
3  |   1    |  1      |   4       |  1     |   2.23
4  |   1    |  1      |   4       |  5     |   6.8
5  |   1    |  1      |   4       |  6     |   1.5

そして、私はこのようなデータを取得するように言われました

ElementID | PhaseID1 | PhaseID5 | PhaseID6
--------------------------------------------
    3     |   NULL   |   6.74   |   8.25
    4     |   2.23   |   6.8    |   1.5

PIVOT関数を使用する必要があることを理解しています。しかし、それを明確に理解することはできません。誰かが上記の場合にそれを説明することができればそれは大きな助けになるでしょう(またはもしあれば他の方法)

回答:


109

PIVOTデータを1つの列から複数の列にローテーションするために使用されます。

あなたの例として、ここにSTATIC Pivotがあります。これは、回転させたい列をハードコーディングすることを意味します。

create table temp
(
  id int,
  teamid int,
  userid int,
  elementid int,
  phaseid int,
  effort decimal(10, 5)
)

insert into temp values (1,1,1,3,5,6.74)
insert into temp values (2,1,1,3,6,8.25)
insert into temp values (3,1,1,4,1,2.23)
insert into temp values (4,1,1,4,5,6.8)
insert into temp values (5,1,1,4,6,1.5)

select elementid
  , [1] as phaseid1
  , [5] as phaseid5
  , [6] as phaseid6
from
(
  select elementid, phaseid, effort
  from temp
) x
pivot
(
  max(effort)
  for phaseid in([1], [5], [6])
)p

これは、動作バージョンのSQLデモです。

これは、列のリストを動的に作成してPIVOTを実行する動的PIVOTを介して実行することもできます。

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX);

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.phaseid) 
            FROM temp c
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT elementid, ' + @cols + ' from 
            (
                select elementid, phaseid, effort
                from temp
           ) x
            pivot 
            (
                 max(effort)
                for phaseid in (' + @cols + ')
            ) p '


execute(@query)

両方の結果:

ELEMENTID   PHASEID1    PHASEID5    PHASEID6
3           Null        6.74        8.25
4           2.23        6.8         1.5

1
ありがとうございます。PhaseIDQUOTENAMEの前にハードコーディングする必要があるのは1つだけです。正しい?
Web-E

1
QUOTENAMEで、値を取得する必要がある列を特定する必要があります。それはあなたが求めていることですか?
タリン

STUFFソリューションを奇妙な列名(スペース、括弧など)で機能させるためにSELECT distinct '],['、私はしなければなりませんでした。また、ステートメントの最後に1, 2, '') + ']'
Nat

@ Web-E、残念ながらそうです。回避策として、アプリケーションでクエリ文字列を記述したり、ストアドプロシージャで動的SQLを操作したりできます。
マーコム

7

これらは非常に基本的なピボットの例です。

SQL SERVER –PIVOTおよびUNPIVOTテーブルの例

製品テーブルの上記のリンクの例:

SELECT PRODUCT, FRED, KATE
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
 PIVOT (SUM(QTY) FOR CUST IN (FRED, KATE)) AS pvt
ORDER BY PRODUCT

レンダリング:

 PRODUCT FRED  KATE
 --------------------
 BEER     24    12
 MILK      3     1
 SODA   NULL     6
 VEG    NULL     5

同様の例は、SQLServerのピボットテーブルに関するブログ投稿にあります。簡単なサンプル


また、ソーステーブルから余分な数値列をプルすると、ピボットによって結果が多くの行に分割されることに注意してください。例SELECT CUST, VEG, SODA FROM (SELECT rand() as x, CUST, PRODUCT, QTY FROM Product) up PIVOT ( SUM(x) FOR PRODUCT IN (VEG, SODA) ) AS pvt ORDER BY CUST GO これを機能させるにはqty、ソースから列を削除する必要があります
RaheelHasan19年

4

ここに追加するものがありますが、誰も言及していません。

このpivot関数は、ソースに3つの列がある場合にうまく機能aggregateしますfor。1つは、、 1つは列として拡散し、もう1つはrow分散のピボットとして使用します。製品の例では、ですQTY, CUST, PRODUCT

ただし、ソースにさらに列がある場合は、追加の列ごとの一意の値に基づいて、ピボットごとに1行ではなく、結果が複数の行に分割されます( Group By単純なクエリの場合のように)。

この例を参照してください。iveはタイムスタンプ列をソーステーブルに追加しました。

ここに画像の説明を入力してください

今、その影響を見てください:

SELECT CUST, MILK

FROM Product
-- FROM (SELECT CUST, Product, QTY FROM PRODUCT) p
PIVOT (
    SUM(QTY) FOR PRODUCT IN (MILK)
) AS pvt

ORDER BY CUST

ここに画像の説明を入力してください


これを修正するには、上記のようにサブクエリをソースとしてプルします。3列のみです(これはシナリオで常に機能するとは限りませんwhere。タイムスタンプの条件を設定する必要があるかどうかを想像してください)。

2番目の解決策は、を使用してgroup by、ピボットされた列の値の合計を再度実行することです。

SELECT 
CUST, 
sum(MILK) t_MILK

FROM Product
PIVOT (
    SUM(QTY) FOR PRODUCT IN (MILK)
) AS pvt

GROUP BY CUST
ORDER BY CUST

GO

ここに画像の説明を入力してください


4

ピボットは、データセット内の列の1つを行から列に変換するために使用されます(これは通常、拡散列と呼ばれます)。与えた例では、これはPhaseID行を列のセットに変換することを意味しますPhaseID。この場合、-1、5、および6を含むことができる個別の値ごとに1つの列があります。

これらのピボット値は、指定した例の列を介してグループ化されていElementIDます。

通常は、拡散値()とグループ化値()の交点によって参照される値を提供する何らかの形式の集計も提供する必要があります。与えられた例では、使用される集計は不明確ですが、PhaseIDElementIDEffort列ます。

このピボットが実行されると、グループ化拡散列を使用して集計値が検索されます。またはあなたの場合、ElementIDそしてPhaseIDXルックアップEffort

使用してグループ化し、拡散、集約用語を使用するには、通常のようにピボットのための例の構文が表示されます。

WITH PivotData AS
(
    SELECT <grouping column>
        , <spreading column>
        , <aggregation column>
    FROM <source table>
)
SELECT <grouping column>, <distinct spreading values>
FROM PivotData
    PIVOT (<aggregation function>(<aggregation column>)
        FOR <spreading column> IN <distinct spreading values>));

これにより、グループ化、拡散、および集計の列がソースからピボットテーブルにどのように変換されるかがグラフィカルに説明されます。


3

互換性エラーを設定するには

ピボット機能を使用する前にこれを使用してください

ALTER DATABASE [dbname] SET COMPATIBILITY_LEVEL = 100  

3
    SELECT <non-pivoted column>,
    [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 will become column headers>]
    IN ( [first pivoted column], [second pivoted column],
    ... [last pivoted column])
) AS <alias for the pivot table>
<optional ORDER BY clause>;

USE AdventureWorks2008R2 ;
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

    -- Pivot table with one row and five columns
SELECT 'AverageCost' AS Cost_Sorted_By_Production_Days, 
[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;




Here is the result set.
Cost_Sorted_By_Production_Days    0         1         2           3       4       
AverageCost                       5.0885    223.88    359.1082    NULL    949.4105

1
なぜ<SELECT query that produces the data>テーブルだけではないのですか?
RaheelHasan19年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.