2013-04-10 60 views
191

嗯,我想我真的很密集。我已閱讀MS透視表上的東西,我仍然有問題得到這個正確的。我看到你們中有些人似乎吃了這些東西,所以我只是決定註冊並提出問題。在SQL Server中使用'Pivot'將行轉換爲列

我有一個正在創建的臨時表,我們會說第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 

商店數量下降的側面和周。

感謝您的幫助。

+0

感謝馬克的代碼重新格式化。我想我已經想通了,我想我沒有。 :-) – Jeff 2013-04-10 16:45:46

+1

這花了我幾個去! :) – 2013-04-10 16:46:37

+0

可能重複的[SQL Server動態PIVOT查詢?](http://stackoverflow.com/questions/10404348/sql-server-dynamic-pivot-query) – RichardTheKiwi 2013-05-03 09:46:29

回答

245

如果您使用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 Demo

然後如果您需要動態生成周數,您的代碼將是:

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 Demo

動態版本生成應該轉換爲列的week數字列表。雙方產生相同的結果:

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

非常好!但是當列的所有值都爲NULL時如何消除列? – ZooZ 2015-01-19 09:22:10

+0

@ ZooZ請參見[答案](http://stackoverflow.com/a/27532568/1028230)。還沒有逐字地嘗試過,但這個概念是完美的。 – ruffin 2015-06-17 15:58:00

+0

+1「這聽起來像你需要使用動態SQL,如果幾周未知,但最初使用硬編碼版本更容易看到正確的代碼。」與Qlikview通用函數(https://community.qlik.com/blogs/qlikviewdesignblog/2014/03/31/generic)不同,它允許您不需要明確指定不同的「FOR ____ IN(...)」 – 2015-08-13 22:03:16

4

這是你可以做什麼:

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

DEMO

20

這是周動態#。

完整的示例在這裏: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 
12

我已經使用子查詢之前達到同樣的事情。所以,如果您的原始表被稱爲StoreCountsByWeek,而你又列出的商店標識一個單獨的表,那麼它應該是這樣的:

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 

一個優點這種方法是語法更加清晰,這讓它更容易加入到其他表中以將其他字段也納入結果中。

我的軼事結果是,在不到一秒的時間內完成對幾千行的查詢,實際上有7個子查詢。但正如在評論中指出的那樣,用這種方法進行計算花費更高,所以如果您希望在大量數據上運行,請注意使用此方法。

+6

它比較容易,但它是一個非常昂貴的操作,對於從表返回的每一行,這些子查詢必須執行一次。 – Greg 2016-07-05 23:30:41

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

我寫的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 '[email protected]_table+''; 
     Exec (@query); 
    End; 

    Select @query='Select distinct '[email protected]+' From '+(case when @ind_tmpTbl=1 then 'tempdb.' else '' end)[email protected]+'.'[email protected]+' where '[email protected]+' 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('[email protected]_agg+'(case when '[email protected]+'='+quotename(@pivot_col,char(39))+' then '[email protected]_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 @[email protected][email protected]_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 '[email protected]_cols+','[email protected]_query_pivot+' into '[email protected]_table+' From '+(case when @ind_tmpTbl=1 then 'tempdb.' else '' end)+ 
    @schema+'.'[email protected]+' Group by '[email protected]_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將返回:

enter image description here