2013-11-21 117 views
22

除了寫入光標讀取每行並將其填充到列中,還有其他替代方法是否需要將每行轉換爲列?SQL Server:將行轉置爲列

TimeSeconds TagID Value 
1378700244 A1 3.75 
1378700245 A1 30 
1378700304 A1 1.2 
1378700305 A2 56 
1378700344 A2 11 
1378700345 A3 0.53 
1378700364 A1 4 
1378700365 A1 14.5 
1378700384 A1 144 
1378700384 A4 10 

列數不固定。

輸出:我剛分配了n/a作爲佔位符,表示該交集中沒有數據。

TimeSec  A1 A2 A3 A4 
1378700244 3.75 n/a n/a n/a 
1378700245 30 n/a n/a n/a 
1378700304 1.2 n/a n/a n/a 
1378700305 n/a 56 n/a n/a 
1378700344 n/a 11 n/a n/a 
1378700345 n/a n/a 0.53 n/a 
1378700364 n/a n/a n/a 4 
1378700365 14.5 n/a n/a n/a 
1378700384 144 n/a n/a 10 

希望你能與我分享一些提示。謝謝。

回答

33

一種方式做到這一點,如果tagID值已知前期是使用條件聚集

SELECT TimeSeconds, 
     COALESCE(MAX(CASE WHEN TagID = 'A1' THEN Value END), 'n/a') A1, 
     COALESCE(MAX(CASE WHEN TagID = 'A2' THEN Value END), 'n/a') A2, 
     COALESCE(MAX(CASE WHEN TagID = 'A3' THEN Value END), 'n/a') A3, 
     COALESCE(MAX(CASE WHEN TagID = 'A4' THEN Value END), 'n/a') A4 
    FROM table1 
GROUP BY TimeSeconds 

,或者如果你有NULL值,而不是'n/a'

SELECT TimeSeconds, 
     MAX(CASE WHEN TagID = 'A1' THEN Value END) A1, 
     MAX(CASE WHEN TagID = 'A2' THEN Value END) A2, 
     MAX(CASE WHEN TagID = 'A3' THEN Value END) A3, 
     MAX(CASE WHEN TagID = 'A4' THEN Value END) A4 
    FROM table1 
GROUP BY TimeSeconds 

PIVOT

OK
SELECT TimeSeconds, A1, A2, A3, A4 
    FROM 
(
    SELECT TimeSeconds, TagID, Value 
    FROM table1 
) s 
PIVOT 
(
    MAX(Value) FOR TagID IN (A1, A2, A3, A4) 
) p 

路輸出(帶有NULL S):

 
TimeSeconds A1  A2  A3 A4 
----------- ------- ------ ----- ----- 
1378700244 3.75 NULL NULL NULL 
1378700245 30.00 NULL NULL NULL 
1378700304 1.20 NULL NULL NULL 
1378700305 NULL 56.00 NULL NULL 
1378700344 NULL 11.00 NULL NULL 
1378700345 NULL NULL 0.53 NULL 
1378700364 4.00 NULL NULL NULL 
1378700365 14.50 NULL NULL NULL 
1378700384 144.00 NULL NULL 10.00 

如果你要搞清楚TagID值進行動態再使用動態SQL

DECLARE @cols NVARCHAR(MAX), @sql NVARCHAR(MAX) 

SET @cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(TagID) 
      FROM Table1 
      ORDER BY 1 
      FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)'),1,1,'') 

SET @sql = 'SELECT TimeSeconds, ' + @cols + ' 
       FROM 
      (
       SELECT TimeSeconds, TagID, Value 
       FROM table1 
      ) s 
      PIVOT 
      (
       MAX(Value) FOR TagID IN (' + @cols + ') 
      ) p' 

EXECUTE(@sql) 
+0

Thansk爲你的想法。我認爲我的知識仍然停留在SQL Server 2000時代。必須開始學習如何使用數據透視表。 – user3015739

9

SQL Server有一個PIVOT命令,可能是你是什麼尋找。

select * from Tag 
pivot (MAX(Value) for TagID in ([A1],[A2],[A3],[A4])) as TagTime; 

如果列不是固定的,您必須將其與某些動態SQL結合使用。

DECLARE @columns AS VARCHAR(MAX); 
DECLARE @sql AS VARCHAR(MAX); 

select @columns = substring((Select DISTINCT ',' + QUOTENAME(TagID) FROM Tag FOR XML PATH ('')),2, 1000); 

SELECT @sql = 

'SELECT * 
FROM TAG 
PIVOT 
(
    MAX(Value) 
    FOR TagID IN(' + @columns + ')) as TagTime;'; 

execute(@sql); 
+0

哇...你的動態SQL岩石。我必須檢查QUOTENAME,XML PATH函數是什麼。謝謝你的樣品。 – user3015739

+0

謝謝。我用空格列名燒了幾次,直到找到QUOTENAME。 – sarme

+0

是的,QUOTENAME是一個很好的功能,我只是不明白爲什麼一些應用程序使用像TIME,VALUE這樣的列名。使用QUOTENAME可以避免這些問題。 – user3015739

1

基礎上solutionbluefeet這裏是使用動態SQL生成轉表的存儲過程。它要求所有字段是除轉置列(列在將成爲報頭中所得到的表)數字:

/****** Object: StoredProcedure [dbo].[SQLTranspose] Script Date: 11/10/2015 7:08:02 PM ******/ 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
-- ============================================= 
-- Author:  Paco Zarate 
-- Create date: 2015-11-10 
-- Description: SQLTranspose dynamically changes a table to show rows as headers. It needs that all the values are numeric except for the field using for  transposing. 
-- Parameters: @TableName - Table to transpose 
--    @FieldNameTranspose - Column that will be the new headers 
-- Usage: exec SQLTranspose <table>, <FieldToTranspose> 
--  table and FIeldToTranspose should be written using single quotes 
-- ============================================= 
ALTER PROCEDURE [dbo].[SQLTranspose] 
    -- Add the parameters for the stored procedure here 
    @TableName NVarchar(MAX) = '', 
    @FieldNameTranspose NVarchar(MAX) = '' 
AS 
BEGIN 
    -- SET NOCOUNT ON added to prevent extra result sets from 
    -- interfering with SELECT statements. 
    SET NOCOUNT ON; 

    DECLARE @colsUnpivot AS NVARCHAR(MAX), 
    @query AS NVARCHAR(MAX), 
    @queryPivot AS NVARCHAR(MAX), 
    @colsPivot as NVARCHAR(MAX), 
    @columnToPivot as NVARCHAR(MAX), 
    @tableToPivot as NVARCHAR(MAX), 
    @colsResult as xml 

    select @tableToPivot = @TableName; 
    select @columnToPivot = @FieldNameTranspose 


    select @colsUnpivot = stuff((select ','+quotename(C.name) 
     from sys.columns as C 
     where C.object_id = object_id(@tableToPivot) and 
      C.name <> @columnToPivot 
     for xml path('')), 1, 1, '') 

    set @queryPivot = 'SELECT @colsResult = (SELECT '','' 
        + quotename('[email protected]+') 
        from '[email protected]+' t 
        where '[email protected]+' <> '''' 
      FOR XML PATH(''''), TYPE)' 

    exec sp_executesql @queryPivot, N'@colsResult xml out', @colsResult out 

    select @colsPivot = STUFF(@colsResult.value('.', 'NVARCHAR(MAX)'),1,1,'') 

    set @query 
    = 'select name, rowid, '[email protected]+' 
     from 
     (
      select '[email protected]+' , name, value, ROW_NUMBER() over (partition by '[email protected]+' order by '[email protected]+') as rowid 
      from '[email protected]+' 
      unpivot 
      (
      value for name in ('[email protected]+') 
     ) unpiv 
     ) src 
     pivot 
     (
      sum(value) 
      for '[email protected]+' in ('[email protected]+') 
     ) piv 
     order by rowid' 
    exec(@query) 
END 
9

另一種選擇,其可以是適合在這種情況下,使用XML

將行轉換爲列的XML選項基本上是PIVOT的最佳版本,因爲它解決了動態列限制問題。

該腳本的XML版本通過使用XML Path,動態T-SQL和一些內置函數(即STUFF,QUOTENAME)的組合來解決此限制。

縱向擴張

類似於樞軸和光標,新增加的政策都能夠在腳本的XML版本中檢索,而不改變原來的腳本。

橫向擴展

不同的是支點,可以在不改變腳本來顯示新添加的文檔。

性能故障

在IO方面的腳本的XML版本的統計數據幾乎是類似PIVOT - 唯一不同的是,XML具有dtTranspose表的第二次掃描,但這次從邏輯讀取數據緩存。

你可以找到一些有關這些解決方案(包括一些實際的T-SQL exmaples)在這篇文章中: https://www.sqlshack.com/multiple-options-to-transposing-rows-into-columns/

0

我有一個稍微不同的要求,因此我不得不選擇轉列成行。

表有列:

create table tbl (ID, PreviousX, PreviousY, CurrentX, CurrentY) 

我需要列PreviousCurrent,和行爲XY。在靜態表中生成笛卡爾積很好地工作,如:

select 
    ID, 
    max(case when metric='X' then PreviousX 
     case when metric='Y' then PreviousY end) as Previous, 
    max(case when metric='X' then CurrentX 
     case when metric='Y' then CurrentY end) as Current 
from tbl inner join 
    /* Cartesian product - transpose by repeating row and 
    picking appropriate metric column for period */ 
    (VALUES (1, 'X'), (2, 'Y')) AS x (sort, metric) ON 1=1 
group by ID 
order by ID, sort