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

时间:2014-06-20 02:55:19

标签: sql-server

在搜索了几种使用PIVOT,交叉连接等将列转换为行的方法后,我的问题仍未得到解答

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

ID | Name | DOB   | BloodGroup | ...... 147 columns
1    XYZ    17MAY    A+          ......

我的目标是将此结果集转换为2列和147行

Column_Name | Value
ID             1
NAME           XYZ
:               :

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

3 个答案:

答案 0 :(得分:1)

我采用了Gordon在帖子中提到的第二种方法,但是从中构建了动态SQL。我CROSS JOIN了几个sys表JOIN和一个源表的结果,然后从列名中建立一个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万行,如果这意味着什么。好处是它可以灵活地无缝地接收任何表格。无论如何,只是觉得这是一个有趣的拼图,所以决定玩一下。希望它有所帮助。

编辑:考虑一下,这可能比戈登提出的方法要慢,但我做得很好。那好吧。 (是的,他的方法在大约一半的时间内起作用。获得幻想对我帮助不大。)

答案 1 :(得分:0)

这称为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_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是更好的方法。语法描述为here

答案 2 :(得分:0)

感谢您的回复。

我想出了一种方法。

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