SQL Server에서 '피벗'을 사용하여 행을 열로 변환


279

MS 피벗 테이블의 내용을 읽었으며 여전히이 문제를 해결하는 데 문제가 있습니다.

생성되는 임시 테이블이 있는데, 열 1은 상점 번호이고 열 2는 주 번호이며 마지막으로 열 3은 일부 유형의 총계입니다. 또한 주 번호는 동적이며 상점 번호는 정적입니다.

Store      Week     xCount
-------    ----     ------
102        1        96
101        1        138
105        1        37
109        1        59
101        2        282
102        2        212
105        2        78
109        2        97
105        3        60
102        3        123
101        3        220
109        3        87

다음과 같이 피벗 테이블로 나오고 싶습니다.

Store        1          2          3        4        5        6....
----- 
101        138        282        220
102         96        212        123
105         37        
109

측면과 주 아래에 숫자를 저장하십시오.


답변:


356

SQL Server 2005+를 사용하는 경우이 PIVOT함수를 사용 하여 데이터를 행에서 열로 변환 할 수 있습니다 .

주를 알 수없는 경우 동적 SQL을 사용해야하는 것처럼 들리지만 처음에 하드 코딩 된 버전을 사용하여 올바른 코드를 보는 것이 더 쉽습니다.

먼저 다음은 빠른 테이블 정의 및 사용 데이터입니다.

CREATE TABLE #yt 
(
  [Store] int, 
  [Week] int, 
  [xCount] int
);

INSERT INTO #yt
(
  [Store], 
  [Week], [xCount]
)
VALUES
    (102, 1, 96),
    (101, 1, 138),
    (105, 1, 37),
    (109, 1, 59),
    (101, 2, 282),
    (102, 2, 212),
    (105, 2, 78),
    (109, 2, 97),
    (105, 3, 60),
    (102, 3, 123),
    (101, 3, 220),
    (109, 3, 87);

값을 알고 있으면 쿼리를 하드 코딩합니다.

select *
from 
(
  select store, week, xCount
  from yt
) src
pivot
(
  sum(xcount)
  for week in ([1], [2], [3])
) piv;

SQL 데모 보기

그런 다음 주 번호를 동적으로 생성해야하는 경우 코드는 다음과 같습니다.

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

select @cols = STUFF((SELECT ',' + QUOTENAME(Week) 
                    from yt
                    group by Week
                    order by Week
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT store,' + @cols + ' from 
             (
                select store, week, xCount
                from yt
            ) x
            pivot 
            (
                sum(xCount)
                for week in (' + @cols + ')
            ) p '

execute(@query);

SQL 데모를 참조하십시오 .

동적 버전 week은 열로 변환해야하는 숫자 목록을 생성합니다 . 둘 다 동일한 결과를 제공합니다.

| STORE |   1 |   2 |   3 |
---------------------------
|   101 | 138 | 282 | 220 |
|   102 |  96 | 212 | 123 |
|   105 |  37 |  78 |  60 |
|   109 |  59 |  97 |  87 |

4
아주 좋아요! 그러나 해당 열의 모든 값이 NULL 일 때 열을 제거하는 방법은 무엇입니까?
ZooZ

1
@ZooZ 아래 답변을 참조하십시오 . 구두로 시도하지는 않았지만 개념은 건전합니다.
ruffin

1
+1 "주를 알 수없는 경우 동적 SQL을 사용해야 할 것 같지만 처음에는 하드 CD 버전을 사용하여 올바른 코드를 보는 것이 더 쉽습니다." Qlikview Generic 함수 ( 커뮤니티 .qlik.com / blogs / qlikviewdesignblog / 2014 / 03 / 31 / generic)와 달리 다른 "FOR ____ IN (...)"의 이름을 명시 적으로 지정할 필요는 없습니다.
The Red Pea

1
초기에 cte로 피벗 테이블을 구축하는 경우. cte3 AS (select ... )그런 다음 위와 같이 정의 된 논리를 @cols가지며 @query... 오류가 있습니다 .` 잘못된 개체 이름 'cte3'.` 어떻게 수정합니까? –
엘리자베스

3
환상적입니다-@bluefeet가 좋습니다. 나는 STUFF(...)전에도 사용하지 않았다 XML PATH. 다른 독자의 이익을 위해 행 이름을 결합하고 선행 쉼표를 자르기 만하면됩니다. 참고 다음은 약간 더 간단하다고 생각합니다. select @cols = (SELECT DISTINCT QUOTENAME (Week) + ','by yt order by 1 FOR XML PATH ( '')) set @cols = SUBSTRING (@cols, 1, LEN ( @cols) - 1) ...를 교체 group by함으로써 distinctorder by 1수동 마 접미사 쉼표를!
DarthPablo 2016 년

26

동적 주 수입니다.

전체 예 : SQL Dynamic Pivot

DECLARE @DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE @ColumnName AS NVARCHAR(MAX)

--Get distinct values of the PIVOT Column 
SELECT @ColumnName= ISNULL(@ColumnName + ',','') + QUOTENAME(Week)
FROM (SELECT DISTINCT Week FROM #StoreSales) AS Weeks

--Prepare the PIVOT query using the dynamic 
SET @DynamicPivotQuery = 
  N'SELECT Store, ' + @ColumnName + ' 
    FROM #StoreSales
    PIVOT(SUM(xCount) 
          FOR Week IN (' + @ColumnName + ')) AS PVTTable'
--Execute the Dynamic Pivot Query
EXEC sp_executesql @DynamicPivotQuery

이봐, 나는 테이블을 동적으로 피벗해야 할 바이올린이 있는데, 당신이 그걸 도와 줄 수 있다고 생각하니? dbfiddle.uk/…
Silly Volley

@SillyVolley는 하나입니다. 피벗하려는 것을 지정하지 않았습니다. 또한 Postgres에서이 작업을 수행 할 수 있는지 알 수 없으므로 SQL Server에서 수행했습니다. dbfiddle.uk/…
Enkode

16

하위 쿼리를 사용하여 이전과 동일한 결과를 얻었습니다. 따라서 원래 테이블의 이름이 StoreCountsByWeek이고 스토어 ID가 나열된 별도의 테이블이있는 경우 다음과 같습니다.

SELECT StoreID, 
    Week1=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=1),
    Week2=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=2),
    Week3=(SELECT ISNULL(SUM(xCount),0) FROM StoreCountsByWeek WHERE StoreCountsByWeek.StoreID=Store.StoreID AND Week=3)
FROM Store
ORDER BY StoreID

이 방법의 한 가지 장점은 구문이 더 명확하고 다른 테이블에 조인하여 다른 필드를 결과로 가져 오는 것이 더 쉽다는 것입니다.

내 일화 결과는 1 초 이내에 완료된 2 천 행에 대해이 쿼리를 실행하고 실제로 7 개의 하위 쿼리가 있다는 것입니다. 그러나 주석에서 언급 했듯이이 방법을 사용하면 계산 비용이 많이 들기 때문에 많은 양의 데이터에서 실행될 것으로 예상되는 경우이 방법을 사용하는 데주의하십시오.


8
더 쉽지만 비용이 많이 드는 작업이므로 해당 하위 쿼리는 테이블에서 반환 된 각 행마다 한 번씩 실행되어야합니다.
Greg

11

이것이 당신이 할 수있는 일입니다 :

SELECT * 
FROM yourTable
PIVOT (MAX(xCount) 
       FOR Week in ([1],[2],[3],[4],[5],[6],[7])) AS pvt

데모


5

이 목적에 유용 할 수있는 sp를 작성하고 있습니다. 기본적 으로이 sp는 테이블을 피벗하고 새 테이블을 피벗하거나 데이터 세트 만 반환합니다.이를 실행하는 방법입니다.

Exec dbo.rs_pivot_table @schema=dbo,@table=table_name,@column=column_to_pivot,@agg='sum([column_to_agg]),avg([another_column_to_agg]),',
        @sel_cols='column_to_select1,column_to_select2,column_to_select1',@new_table=returned_table_pivoted;

참고하시기 바랍니다 매개 변수 @agg에 열 이름이 함께해야 '['하고, 매개 변수는 쉼표로 끝나야합니다','

SP

Create Procedure [dbo].[rs_pivot_table]
    @schema sysname=dbo,
    @table sysname,
    @column sysname,
    @agg nvarchar(max),
    @sel_cols varchar(max),
    @new_table sysname,
    @add_to_col_name sysname=null
As
--Exec dbo.rs_pivot_table dbo,##TEMPORAL1,tip_liq,'sum([val_liq]),sum([can_liq]),','cod_emp,cod_con,tip_liq',##TEMPORAL1PVT,'hola';
Begin

    Declare @query varchar(max)='';
    Declare @aggDet varchar(100);
    Declare @opp_agg varchar(5);
    Declare @col_agg varchar(100);
    Declare @pivot_col sysname;
    Declare @query_col_pvt varchar(max)='';
    Declare @full_query_pivot varchar(max)='';
    Declare @ind_tmpTbl int; --Indicador de tabla temporal 1=tabla temporal global 0=Tabla fisica

    Create Table #pvt_column(
        pivot_col varchar(100)
    );

    Declare @column_agg table(
        opp_agg varchar(5),
        col_agg varchar(100)
    );

    IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(@table) AND type in (N'U'))
        Set @ind_tmpTbl=0;
    ELSE IF OBJECT_ID('tempdb..'+ltrim(rtrim(@table))) IS NOT NULL
        Set @ind_tmpTbl=1;

    IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(@new_table) AND type in (N'U')) OR 
        OBJECT_ID('tempdb..'+ltrim(rtrim(@new_table))) IS NOT NULL
    Begin
        Set @query='DROP TABLE '+@new_table+'';
        Exec (@query);
    End;

    Select @query='Select distinct '+@column+' From '+(case when @ind_tmpTbl=1 then 'tempdb.' else '' end)+@schema+'.'+@table+' where '+@column+' is not null;';
    Print @query;

    Insert into #pvt_column(pivot_col)
    Exec (@query)

    While charindex(',',@agg,1)>0
    Begin
        Select @aggDet=Substring(@agg,1,charindex(',',@agg,1)-1);

        Insert Into @column_agg(opp_agg,col_agg)
        Values(substring(@aggDet,1,charindex('(',@aggDet,1)-1),ltrim(rtrim(replace(substring(@aggDet,charindex('[',@aggDet,1),charindex(']',@aggDet,1)-4),')',''))));

        Set @agg=Substring(@agg,charindex(',',@agg,1)+1,len(@agg))

    End

    Declare cur_agg cursor read_only forward_only local static for
    Select 
        opp_agg,col_agg
    from @column_agg;

    Open cur_agg;

    Fetch Next From cur_agg
    Into @opp_agg,@col_agg;

    While @@fetch_status=0
    Begin

        Declare cur_col cursor read_only forward_only local static for
        Select 
            pivot_col 
        From #pvt_column;

        Open cur_col;

        Fetch Next From cur_col
        Into @pivot_col;

        While @@fetch_status=0
        Begin

            Select @query_col_pvt='isnull('+@opp_agg+'(case when '+@column+'='+quotename(@pivot_col,char(39))+' then '+@col_agg+
            ' else null end),0) as ['+lower(Replace(Replace(@opp_agg+'_'+convert(varchar(100),@pivot_col)+'_'+replace(replace(@col_agg,'[',''),']',''),' ',''),'&',''))+
                (case when @add_to_col_name is null then space(0) else '_'+isnull(ltrim(rtrim(@add_to_col_name)),'') end)+']'
            print @query_col_pvt
            Select @full_query_pivot=@full_query_pivot+@query_col_pvt+', '

            --print @full_query_pivot

            Fetch Next From cur_col
            Into @pivot_col;        

        End     

        Close cur_col;
        Deallocate cur_col;

        Fetch Next From cur_agg
        Into @opp_agg,@col_agg; 
    End

    Close cur_agg;
    Deallocate cur_agg;

    Select @full_query_pivot=substring(@full_query_pivot,1,len(@full_query_pivot)-1);

    Select @query='Select '+@sel_cols+','+@full_query_pivot+' into '+@new_table+' From '+(case when @ind_tmpTbl=1 then 'tempdb.' else '' end)+
    @schema+'.'+@table+' Group by '+@sel_cols+';';

    print @query;
    Exec (@query);

End;
GO

다음은 실행 예입니다.

Exec dbo.rs_pivot_table @schema=dbo,@table=##TEMPORAL1,@column=tip_liq,@agg='sum([val_liq]),avg([can_liq]),',@sel_cols='cod_emp,cod_con,tip_liq',@new_table=##TEMPORAL1PVT;

그런 다음 Select * From ##TEMPORAL1PVT반환합니다

여기에 이미지 설명을 입력하십시오


2
select * from (select name, ID from Empoyee) Visits
    pivot(sum(ID) for name
    in ([Emp1],
    [Emp2],
    [Emp3]
    ) ) as pivottable;

2

위의 @Tayrn 답변 개정판은 피벗을 좀 더 쉽게 이해하는 데 도움이 될 수 있습니다.

이것이 최선의 방법은 아니지만 테이블 피벗 방법에 대해 머리를 감싸는 데 도움이되었습니다.

ID = 피벗하려는 행

MY_KEY = 피벗하려는 열 이름이 포함 된 원본 테이블에서 선택하는 열입니다.

VAL = 각 열에서 반환하려는 값입니다.

MAX (VAL) => 다른 집계 함수로 대체 할 수 있습니다. SUM (VAL), MIN (VAL) 등

DECLARE @cols AS NVARCHAR(MAX),
@query  AS NVARCHAR(MAX)
select @cols = STUFF((SELECT ',' + QUOTENAME(MY_KEY) 
                from yt
                group by MY_KEY
                order by MY_KEY ASC
        FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)') 
    ,1,1,'')
set @query = 'SELECT ID,' + @cols + ' from 
         (
            select ID, MY_KEY, VAL 
            from yt
        ) x
        pivot 
        (
            sum(VAL)
            for MY_KEY in (' + @cols + ')
        ) p '

        execute(@query);

2

다른 데이터베이스가이 문제를 해결하는 방법에 대한 아이디어를 제공하십시오. DolphinDB또한 피벗을 기본적으로 지원하며 SQL이 훨씬 직관적이고 깔끔하게 보입니다. 키 열 ( Store), 피벗 열 ( Week) 및 계산 된 메트릭 ( sum(xCount)) 을 지정하는 것만 큼 간단 합니다.

//prepare a 10-million-row table
n=10000000
t=table(rand(100, n) + 1 as Store, rand(54, n) + 1 as Week, rand(100, n) + 1 as xCount)

//use pivot clause to generate a pivoted table pivot_t
pivot_t = select sum(xCount) from t pivot by Store, Week

DolphinDB는 컬럼 형 고성능 데이터베이스입니다. 데모의 계산 비용은 dell xps 랩탑 (i7 cpu)에서 546ms에 불과합니다. 자세한 내용은 온라인 DolphinDB 매뉴얼 https://www.dolphindb.com/help/index.html?pivotby.html을 참조하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.