使用动态SQL重新格式化表,使列名称为行

时间:2017-03-27 14:20:11

标签: sql sql-server dynamic-sql

我已经坚持了一段时间,而且我没有在网站上找到回答这类内容的东西,所以如果现有问题存在,请指出正确的方向。在SQL Server 2012中,我有一个以ID作为主键的表:

ID      col1     col2   col3  ....
---   ----      -----   -----
1        a        z       k
2        g        b       p
3        k        d       a

我不知道表的长度,也不知道列/列名的数量 但是我希望能得到一张能给我这样的表格:

ID     ColName  Value  
---   ----      -----  
1     col1      a 
1     col2      z
1     col3      k
2     col1      g
2     col2      b
2     col3      p
3     col1      k
3     col2      d
3     col3      a 
...

我知道

SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE 
 TABLE_NAME = 'table'

获取我的列,我尝试使用它来创建临时表 将我想要的格式插入到临时表中,但我不确定如何遍历表中的每一行,然后为每个列名动态获取所需的值并显示它。我已经能够用双游标实现这一点,但这很慢,而且我不确定如何处理这个问题,因为我在SQL方面相对较新。任何帮助将不胜感激!

修改

非常感谢Lamak!我确实有不同的数据类型,并将它们转换为varchars现在告诉我这个概念确实有效。但是,我有4种常见的数据类型(varchar,float,int,datetime),所以我有4个值字段,每个我将根据它将列值插入其中一个4并保留其他3个空白。我知道INFORMATION_SCHEMA.COLUMNS也提供了data_types,所以我想知道基于简单的IF语句转换“STUFF”变量中的数据类型的语法是什么。我尝试将data_types映射到列名,但是使用任何类型的条件语句都会破坏查询。如果有人有一个简单的例子,那就太好了:)

修改 实际上,我已经能够弄清楚我需要为每种数据类型创建4个变量,而不是仅在其中一个数据类型中创建它们。谢谢大家的帮助!

2 个答案:

答案 0 :(得分:1)

正如评论所说,你需要使用动态unpivot。

如果ID旁边的每个列都具有相同的数据类型,则可以使用以下查询:

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


SELECT @colsUnpivot = STUFF((SELECT ', ' + QUOTENAME(C.name)
                             FROM sys.columns as C
                             WHERE C.object_id = object_id('table') AND
                                   C.name <> 'ID'
                             FOR XML PATH('')), 1, 1, '');

SET @query = '
SELECT  ID, 
        ColName, 
        Value
FROM 
(
  SELECT *
  FROM dbo.table
) x
UNPIVOT
(
  Value FOR ColName IN (' + @colsunpivot + ')
) u
';

EXEC(@query);

现在,如果数据类型不同,那么您需要先将每个列转换为通用数据类型。在以下示例中,我将使用NVARCHAR(1000),但您需要将它们转换为正确的数据类型:

DECLARE @colsUnpivot1 AS NVARCHAR(MAX),
        @colsUnpivot2 as  NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX);


SELECT @colsUnpivot1 = STUFF((SELECT ', ' + QUOTENAME(C.name)
                             FROM sys.columns as C
                             WHERE C.object_id = object_id('table') AND
                                   C.name <> 'ID'
                             FOR XML PATH('')), 1, 1, '');

SELECT @colsUnpivot2 = STUFF((SELECT ', CONVERT(NVARCHAR(1000),' + QUOTENAME(C.name)
                                     + ') ' + QUOTENAME(C.name)
                             FROM sys.columns as C
                             WHERE C.object_id = object_id('table') AND
                                   C.name <> 'ID'
                             FOR XML PATH('')), 1, 1, '');

SET @query = '
SELECT  ID, 
        ColName, 
        Value
FROM 
(
  SELECT ID, ' + @colsUnpivot2 + '
  FROM dbo.table
) x
UNPIVOT
(
  Value FOR ColName IN ('+ @colsunpivot1 +')
) u
';

EXEC(@query);

答案 1 :(得分:0)

你不必去DYNAMIC。带有CROSS APPLY和一点XML的另一个选项。

UnPivot性能更高,但你会发现这种方法的表现非常可观。

这种方法的另一个好处是Connection keep-alive Content-Length 9437 Content-Type text/html;charset=UTF-8 Date Tue, 28 Mar 2017 21:16:46 GMT 可以是任何查询(不限于表格)。例如From @YourTable A

From ( -- Your Complex Query --) A

返回

Declare @YourTable table (ID int,Col1 varchar(25),Col2 varchar(25),Col3 varchar(25))
Insert Into @YourTable values
 (1,'a','z','k')
,(2,'g','b','p')
,(3,'k','d','a')


Select C.*
 From  @YourTable A
 Cross Apply (Select XMLData=cast((Select A.* for XML Raw) as xml)) B
 Cross Apply (
                Select ID     = r.value('@ID','int')
                      ,Item   = attr.value('local-name(.)','varchar(100)')
                      ,Value  = attr.value('.','varchar(max)') 
                 From  B.XMLData.nodes('/row') as A(r)
                 Cross Apply A.r.nodes('./@*') AS B(attr)
                 Where attr.value('local-name(.)','varchar(100)') not in ('ID','OtherFieldsToExclude')
             ) C