SQL Server 2008 中的 PIVOT / UNPIVOT

Posted

技术标签:

【中文标题】SQL Server 2008 中的 PIVOT / UNPIVOT【英文标题】:PIVOT / UNPIVOT in SQL Server 2008 【发布时间】:2010-04-20 23:37:30 【问题描述】:

我有如下子/父表。

主表:

MasterID, Description

子表

ChildID, MasterID, Description.

使用 PIVOT / UNPIVOT 如何在单行中获得如下结果。

if (MasterID : 1 got x child records)

MasterID, ChildID1, Description1, ChildID2, Description2....... ChildIDx, Descriptionx

谢谢

【问题讨论】:

【参考方案1】:

这是一个 T_SQL,假设如下:

您不知道结果中可能出现多少列。 Pivot 元素可以变化(这就是第一个假设的原因)。 您需要特定的顺序 'ChildId1, ChilDesc1, ChildId2, ChildDesc2... asd so ever'

声明 @MaxCountOfChild 整数

-- Obtaining Maximum times a Master is used by its children
SELECT TOP 1 @MaxCountOfChild= count(*)
FROM ChildTable
GROUP BY MasterID
order by count(*) DESC


--With that number, create a string for the Pivot elements
--if you want them in the order Id1-Desc1-Id2-Desc2
DECLARE 
    @AuxforReplacing nvarchar(MAX),
    @ChildIdsandDescs nvarchar(MAX),
    @PivotElements nvarchar(MAX),
    @Counter int,
    @sql nvarchar(MAX)

SET @Counter=0
SET @AuxforReplacing=''
SET @ChildIdsandDescs=''
SET @PivotElements=''

WHILE (@Counter < @MaxCountOfChild)
begin
    SET @Counter=@Counter +1
    SET @PivotElements=@PivotElements + '[' +convert(varchar, @Counter)+ '],' 
    SET @AuxforReplacing=@AuxforReplacing +  '[' +convert(varchar, @Counter)+ '] as ' + convert(varchar, @Counter) + ','
    SET @ChildIdsandDescs=@ChildIdsandDescs + '[ChildID' + convert(varchar, @Counter)+ '],[ChildDesc' + convert(varchar, @Counter) +'],'

end
SET @PivotElements=LEFT(@PivotElements, len(@PivotElements)-1)
SET @ChildIdsandDescs=LEFT(@ChildIdsandDescs, len(@ChildIdsandDescs)-1)
SET @AuxforReplacing=LEFT(@AuxforReplacing, len(@AuxforReplacing)-1)


--print REPLACE(@AuxforReplacing, 'as ', 'as ChildId')

--print @ChildIds
--print @PivotElements


SET @sql = N'
WITH AuxTable (Masterdesc,ChildId, MasterId,ChildDesc,  NumeroenMaster) 
AS
(
SELECT M.Description as MasterDesc, C.*, RANK() OVER (PARTITION BY M.MasterId ORDER BY M.MasterId, ChildId)
FROM  MasterTable M
    INNER JOIN ChildTable C
        ON M.MasterId=C.MasterId
)

SELECT TablaMaster.MasterId,' + @ChildIdsandDescs + '
FROM 
(
    SELECT MasterId, ' + REPLACE(@AuxforReplacing, 'as ', 'as ChildId') + '
    FROM (
    SELECT MasterId, NumeroenMaster, ChildId
    FROM AuxTable) P
    PIVOT
    (
    MAX (ChildId)
    FOR NumeroenMaster IN (' + @PivotElements +')
    ) AS pvt) As TablaMaster
INNER JOIN 
(
    SELECT MasterId, ' + REPLACE(@AuxforReplacing, 'as ', 'as ChildDesc') + '
    FROM (
    SELECT MasterId, NumeroenMaster, ChildDesc
    FROM AuxTable) P
    PIVOT
    (
    MAX (ChildDesc)
    FOR NumeroenMaster IN (' + @PivotElements +')
    ) AS pvt) As TablaChild
ON TablaMaster.MasterId= TablaChild.MasterId'

EXEC sp_executesql @sql

编辑:结果是这样的:

MasterId ChildID1 ChildDesc1 ChildID2 ChildDesc2  ChildID3 ChildDesc3 ChildID4 ChildDesc4
-------- -------- ---------- -------- ----------- -------- ---------- -------- ---------
1           1      Child1       2      Child2     NULL        NULL       NULL      NULL
2           3      Child3       4      Child4      7          Child7      8      Child8
3           5      Child5       6      Child5     NULL        NULL       NULL      NULL

Asumming this in the table ChildTable:
ChildId  MasterId  ChildDesc
-------  --------  ---------
1      1       Child1
2      1       Child2
3      2       Child3
4      2       Child4
5      3       Child5
6      3       Child5
7      2       Child7
8      2       Child8

【讨论】:

【参考方案2】:

很大程度上取决于交叉表列的数量是否固定。如果是,那么您可以简单地执行以下操作:

Select ParentDesc
    , [1] As ChildId1
    , [Description1] As ChildDescription1
    , [2] As ChildId2
    , [Description2] As ChildDescription2
    , [3] As ChildId3
    , [Description3] As ChildDescription3
From    (
        Select C.Id As ChildId, C.Description As ChildDesc, P.Description As ParentDesc
        From ChildItems As C
            Join ParentItems As P
                On P.Id = C.ParentId
        ) As C
Pivot   (
        Count(ChildId)
        For ChildId In([1],[2],[3])
        ) As PVT0
Pivot   (
        Count(ChildDesc)
        For ChildDesc In([Descripion1],[Descripion2],[Descripion3])
        ) As PVT1

还有一种方法可以使用CASE 函数来实现类似的结果。

但是,如果您想要在运行时确定交叉表列的数量,那么在 SQL Server 内部执行此操作的唯一方法是使用一些非常动态的 SQL。这超出了 SQL Server 提供数据(而不是信息)的主要目的的范围。如果您需要动态交叉表,我建议您不要在 SQL Server 中执行此操作,而是使用报告工具或在中间层组件中构建您的结果集。

【讨论】:

我不知道你可以使用两个枢轴和一个选择结果!这太棒了!

以上是关于SQL Server 2008 中的 PIVOT / UNPIVOT的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL Server 2008 中使用 PIVOT

在 MS SQL Server 2008 的 Pivot 中计算水平总计

SQL Server 2008 R2 使用 PIVOT 和 varchar 列不起作用

如何在 Sql Server 2008 R2 中将列转换为行?

获取 ROWS 作为 COLUMNS(SQL Server 动态 PIVOT 查询)

将txt文件导入sql server 2008数据库中,出现警告 0x80049304: