Sql server 动态sql将具有一行的列名转换为具有两列和多行的表

Sql server 动态sql将具有一行的列名转换为具有两列和多行的表,sql-server,Sql Server,在使用PIVOT、cross-join等搜索了几种将列转换为行的方法之后,我的问题仍然没有得到回答 我有一个返回1行和147列的结果集 ID | Name | DOB | BloodGroup | ...... 147 columns 1 XYZ 17MAY A+ ...... 我的目标是将此结果集转换为2列和147行 Column_Name | Value ID 1 NAME XYZ :

在使用PIVOT、cross-join等搜索了几种将列转换为行的方法之后,我的问题仍然没有得到回答

我有一个返回1行和147列的结果集

ID | Name | DOB   | BloodGroup | ...... 147 columns
1    XYZ    17MAY    A+          ......
我的目标是将此结果集转换为2列和147行

Column_Name | Value
ID             1
NAME           XYZ
:               :

我该怎么做呢?感谢您的反馈

这被称为
unpivot
。从概念上讲,最简单的方法是:

select 'id' as column_name, cast(id as varchar(255)) as column_value
from Result
union all
select 'name', name
from Result
union all
. . .
这可能会很麻烦。如果
result
是一个表,则可以使用
information\u schema.columns
创建SQL,类似于:

select 'select ''' + column_name + ''' as column_name, cast(' + column_name + ' as varchar(255)) as column_value from result union all'
from information_schema.columns
where table_name = 'Result'

此方法不是最有效的方法,因为它需要读取每列的表。因此,
unpivot
是更好的方法。语法已被描述。

我采用了Gordon在其文章中提到的第二种方法,但从中构建了动态SQL。我交叉连接了几个sys表连接的结果和一个源表,然后根据列名构建了一个CASE语句。我将其合并为动态SQL,然后执行它。为了简单起见,我将所有变量项都变成了变量,您可以在例程开始时填写这些变量。代码如下:

USE AdventureWorks2012;
GO

DECLARE @MySchema VARCHAR(100),  
        @MyTable VARCHAR(100),
        @MyUIDColumn VARCHAR(100),
        @MyFieldsMaxLength VARCHAR(10),
        @SQL AS VARCHAR(MAX);

SET @MySchema = 'Person';
SET @MyTable = 'Person';

-- Unique ID which routine will identify unique entities by.  Will also sort on this value in the end result dataset.
SET @MyUIDColumn = 'BusinessEntityID';

-- This determines the max length of the fields you will cast in your Value column.
SET @MyFieldsMaxLength = 'MAX';

WITH cteSQL
AS
(
    SELECT      1 AS Sorter, 'SELECT c.name AS ColumnName,' AS SQL

    UNION ALL

    SELECT      2, 'CASE' AS Statement

    UNION ALL

    SELECT      3, 'WHEN c.name = ''' + c.name + ''' THEN CAST(mt.' + c.name + ' AS VARCHAR(' + @MyFieldsMaxLength + ')) '
    FROM        sys.tables t INNER JOIN sys.columns c
                ON t.object_id = c.object_id
    WHERE       t.name = @MyTable

    UNION ALL

    SELECT      4, 'END AS Value' AS Statement

    UNION ALL

    SELECT      5, 'FROM sys.tables t INNER JOIN sys.columns c ON t.object_id = c.object_id INNER JOIN sys.schemas s ON t.schema_id = s.schema_id, ' + @MySchema + '.' + @MyTable + ' mt WHERE t.name = ''' + @MyTable + ''' AND s.name = ''' + @MySchema + ''' ORDER BY mt. ' + @MyUIDColumn + ', c.name;' 
)

SELECT @SQL =
(
    SELECT      SQL + ' ' 
    FROM        cteSQL
    ORDER BY    Sorter
    FOR XML PATH ('')
);

EXEC(@SQL);
我真的说不出执行时间会是什么样子。我在本地机器上对AdventureWorks2012 Person.Person表(约20k行,13列)运行了它,它在大约8秒内恢复了约250万行,如果这意味着什么的话。好的是,它可以灵活地无缝地处理任何表格。不管怎样,我只是觉得这是一个有趣的拼图游戏,所以决定玩一玩。希望能有帮助


编辑:仔细想想,这可能比Gordon提出的方法还要慢,但我确实做到了。哦,好吧。(是的,他的方法在大约一半的时间内都能奏效。对我没有多大帮助。)

谢谢你的回复

我想出了一个办法


我在一个逗号分隔的字符串变量中得到了所有列名。2.将相同的字符串传递给UNPIVOT对象。通过这种方法,完全避免了对140个列名进行硬编码

您期望的样本输出是什么?目前还不太清楚。是否要在第二个输出列中显示列名和列值?它将分别有两列,即ColumnName和value。ColumnName下是所有列名称,如ID、Name、DOB等,其中values列中是它们各自的值。您试图实现的是什么,而行形式的数据无法实现?请从information_schema.columns where table_Name='XYZ'中选择column_Name。此查询提供一行下的所有列名。我需要弄清楚的是,如何提取与列名对应的值。至于Daniel的回答,答案是否定的。我特别需要该结构,它需要连接到另一个表。我已经想到了这种方法。然而,这并不是最好的方法。谢谢你的努力!