2013-04-10 78 views
191

Vâng, tôi đoán tôi thực sự dày đặc. Tôi đã đọc nội dung trên bảng tổng hợp MS và tôi vẫn gặp sự cố khi thực hiện điều này đúng. Tôi đã thấy một số bạn dường như ăn và ngủ những thứ này, vì vậy tôi quyết định đăng ký và đặt câu hỏi.Chuyển đổi hàng thành cột bằng cách sử dụng 'Pivot' trong SQL Server

Tôi có bảng tạm thời đang được tạo, chúng tôi sẽ nói rằng cột 1 là Số cửa hàng và cột 2 là số tuần và cuối cùng cột 3 là tổng số loại. Ngoài ra các số Tuần là động, số cửa hàng là tĩnh.

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 

Tôi muốn nó đi ra như một bảng pivot, như thế này:

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

số cửa hàng xuống bên cạnh và tuần trên đầu.

Cảm ơn sự giúp đỡ.

+0

Cảm ơn Đánh dấu cho định dạng lại mã. Tôi nghĩ rằng tôi đã tìm ra, tôi đoán là tôi không biết. :-) – Jeff

+1

Nó đã cho tôi một vài đi! :) –

+0

có thể trùng lặp với [truy vấn PIVOT động SQL Server?] (Http://stackoverflow.com/questions/10404348/sql-server-dynamic-pivot-query) – RichardTheKiwi

Trả lời

245

Nếu bạn đang sử dụng SQL Server 2005+, thì bạn có thể sử dụng hàm PIVOT để chuyển đổi dữ liệu từ hàng thành cột.

Có vẻ như bạn sẽ cần phải sử dụng sql động nếu tuần không xác định nhưng sẽ dễ dàng hơn để xem mã chính xác bằng cách sử dụng phiên bản được mã hóa cứng ban đầu.

Đầu tiên, đây là một số định nghĩa nhanh bảng và dữ liệu để sử dụng:

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); 

Nếu giá trị của bạn được biết đến, sau đó bạn sẽ mã hóa cứng các truy vấn:

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

Xem SQL Demo

Sau đó, nếu bạn cần tạo số tuần tự động, mã của bạn sẽ là:

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); 

Xem SQL Demo.

Phiên bản động, tạo danh sách số week cần được chuyển đổi thành cột. Cả hai đưa ra cùng một kết quả:

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

Rất đẹp! Nhưng làm thế nào để loại bỏ cột khi tất cả các giá trị của cột đó là NULL? – ZooZ

+0

@ZooZ Xem [answer below] (http://stackoverflow.com/a/27532568/1028230). Đã không thử nó ra nguyên văn, nhưng khái niệm là âm thanh. – ruffin

+0

+1 "Có vẻ như bạn sẽ cần sử dụng sql động nếu các tuần không xác định nhưng sẽ dễ dàng hơn khi xem mã chính xác bằng cách sử dụng phiên bản cứng đầu tiên". Không giống như chức năng Qlikview Generic (https://community.qlik.com/blogs/qlikviewdesignblog/2014/03/31/generic) cho phép không yêu cầu bạn đặt tên rõ ràng cho "FOR ____ IN (...)" khác nhau –

4

Đây là những gì bạn có thể làm:

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

DEMO

20

này là dành cho động # tuần.

Full ví dụ ở đây: 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

tôi đã đạt được điều tương tự trước bằng cách sử dụng truy vấn con. Vì vậy, nếu bảng ban đầu của bạn được gọi là StoreCountsByWeek, và bạn đã có một bảng riêng biệt mà liệt kê các ID Store, sau đó nó sẽ trông như thế này:

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 

Một lợi thế để phương pháp này là cú pháp là rõ ràng hơn và nó làm cho nó dễ dàng hơn để tham gia vào các bảng khác để kéo các trường khác vào kết quả.

Kết quả giai thoại của tôi là chạy truy vấn này trên một vài nghìn hàng được hoàn thành trong chưa đầy một giây và tôi thực sự có 7 truy vấn phụ. Nhưng như đã lưu ý trong các bình luận, nó tốn kém hơn về mặt tính toán để thực hiện theo cách này, vì vậy hãy cẩn thận khi sử dụng phương pháp này nếu bạn mong đợi nó chạy trên một lượng lớn dữ liệu.

+6

nó dễ dàng hơn, nhưng nó là một hoạt động rất tốn kém, các truy vấn con đó phải được thực hiện một lần cho mỗi hàng được trả về từ bảng. – Greg

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

Tôi đang viết một sp mà có thể hữu ích cho mục đích này, về cơ bản sp này trục bất kỳ bảng và trả về một bảng mới xoay hoặc trả lại chỉ là tập hợp các dữ liệu, đây là cách để thực hiện điều đó:

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; 

xin lưu ý rằng trong tham số @agg tên cột phải với '[' và tham số phải kết thúc bằng dấu phẩy ','

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 

Đây là một ví dụ về thực hiện:

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; 

sau đó Select * From ##TEMPORAL1PVT sẽ trở lại:

enter image description here

Các vấn đề liên quan