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的主要内容,如果未能解决你的问题,请参考以下文章
在 MS SQL Server 2008 的 Pivot 中计算水平总计
SQL Server 2008 R2 使用 PIVOT 和 varchar 列不起作用
如何在 Sql Server 2008 R2 中将列转换为行?