将1行表展平为键值对表

kat*_*roh 9 sql t-sql pivot sql-server-2008

获取表示连续列值的键值对结果集的最佳方法是什么?

给出下面的表A只有1行


Column1 Column2 Column3 ...
Value1  Value2  Value3
Run Code Online (Sandbox Code Playgroud)

我想查询它并插入另一个表B:


Key                  Value
Column1              Value1
Column2              Value2
Column3              Value3
Run Code Online (Sandbox Code Playgroud)

表A中的一组列事先不知道.

注意:我正在查看FOR XML和PIVOT功能以及动态SQL来执行以下操作:


    DECLARE @sql nvarchar(max)
    SET @sql = (SELECT STUFF((SELECT ',' + column_name 
                              FROM INFORMATION_SCHEMA.COLUMNS 
                              WHERE table_name='TableA' 
                              ORDER BY column_name FOR XML PATH('')), 1, 1, ''))
    SET @sql = 'SELECT ' + @sql + ' FROM TableA'
    EXEC(@sql)
Run Code Online (Sandbox Code Playgroud)

Mik*_*son 18

没有动态的版本.如果您的列名无效,无法用作XML中的元素名称,则会失败.

select T2.N.value('local-name(.)', 'nvarchar(128)') as [Key],
       T2.N.value('text()[1]', 'nvarchar(max)') as Value
from (select *
      from TableA
      for xml path(''), type) as T1(X)
  cross apply T1.X.nodes('/*') as T2(N)
Run Code Online (Sandbox Code Playgroud)

一份工作样本:

declare @T table
(
  Column1 varchar(10), 
  Column2 varchar(10), 
  Column3 varchar(10)
)

insert into @T values('V1','V2','V3')

select T2.N.value('local-name(.)', 'nvarchar(128)') as [Key],
       T2.N.value('text()[1]', 'nvarchar(max)') as Value
from (select *
      from @T
      for xml path(''), type) as T1(X)
  cross apply T1.X.nodes('/*') as T2(N)
Run Code Online (Sandbox Code Playgroud)

结果:

Key                  Value
-------------------- -----
Column1              V1
Column2              V2
Column3              V3
Run Code Online (Sandbox Code Playgroud)

更新

对于具有多个表的查询,您可以使用它for xml auto来获取XML中的表名.请注意,如果在查询中使用表名的别名,则会获得别名.

select X2.N.value('local-name(..)', 'nvarchar(128)') as TableName,
       X2.N.value('local-name(.)', 'nvarchar(128)') as [Key],
       X2.N.value('text()[1]', 'nvarchar(max)') as Value
from (
     -- Your query starts here
     select T1.T1ID,
            T1.T1Col,
            T2.T2ID,
            T2.T2Col
     from T1
       inner join T2
         on T1.T1ID = T2.T1ID
     -- Your query ends here
     for xml auto, elements, type     
     ) as X1(X)
  cross apply X1.X.nodes('//*[text()]') as X2(N)
Run Code Online (Sandbox Code Playgroud)

SQL小提琴

  • 我刚刚为`FOR XML PATH(''),ELEMENTS XSINIL,TYPE添加了`ELEMENTS XSINIL`,因为T1(X)`包含NULL的列.谢谢 (2认同)

8kb*_*8kb 6

我想你已到了一半.只需使用UNPIVOTdynamic SQL马丁推荐:

CREATE TABLE TableA (
  Code VARCHAR(10),
  Name VARCHAR(10),
  Details VARCHAR(10)
) 

INSERT TableA VALUES ('Foo', 'Bar', 'Baz') 
GO

DECLARE @sql nvarchar(max)
SET @sql = (SELECT STUFF((SELECT ',' + column_name 
                          FROM INFORMATION_SCHEMA.COLUMNS 
                          WHERE table_name='TableA' 
                          ORDER BY ordinal_position FOR XML PATH('')), 1, 1, ''))

SET @sql = N'SELECT [Key], Val FROM (SELECT ' + @sql + ' FROM TableA) x '
+ 'UNPIVOT ( Val FOR [Key] IN (' + @sql + ')) AS unpiv'
EXEC (@sql)
Run Code Online (Sandbox Code Playgroud)

结果:

Key          Val
------------ ------------
Code         Foo
Name         Bar
Details      Baz
Run Code Online (Sandbox Code Playgroud)

当然有一个警告.您的所有列都需要与上述代码相同的数据类型才能工作.如果不是,您将收到此错误:

Msg 8167, Level 16, State 1, Line 1
The type of column "Col" conflicts with the type of 
other columns specified in the UNPIVOT list.
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,您需要创建两个列字符串语句.一个用于获取列,另一个用于将它们全部转换为Val列的数据类型.

对于多列类型:

CREATE TABLE TableA (
  Code INT,
  Name VARCHAR(10),
  Details VARCHAR(10)
) 

INSERT TableA VALUES (1, 'Foo', 'Baf') 
GO

DECLARE 
  @sql nvarchar(max),
  @cols nvarchar(max),
  @conv nvarchar(max) 

SET @cols = (SELECT STUFF((SELECT ',' + column_name 
                          FROM INFORMATION_SCHEMA.COLUMNS 
                          WHERE table_name='TableA' 
                          ORDER BY ordinal_position FOR XML PATH('')), 1, 1, ''))

SET @conv = (SELECT STUFF((SELECT ', CONVERT(VARCHAR(50), ' 
                          + column_name + ') AS ' + column_name
                          FROM INFORMATION_SCHEMA.COLUMNS 
                          WHERE table_name='TableA' 
                          ORDER BY ordinal_position FOR XML PATH('')), 1, 1, ''))


SET @sql = N'SELECT [Key], Val FROM (SELECT ' + @conv + ' FROM TableA) x '
+ 'UNPIVOT ( Val FOR [Key] IN (' + @cols + ')) AS unpiv'
EXEC (@sql)
Run Code Online (Sandbox Code Playgroud)