SQL Server 2008 中的 PIVOT/UNPIVOT

发布于 2024-08-30 03:56:36 字数 366 浏览 1 评论 0原文

我得到的子/父表如下。

MasterTable:

MasterID, Description

ChildTable

ChildID, MasterID, Description.

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

if (MasterID : 1 有 x 子记录)

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

谢谢

I got child / parent tables as below.

MasterTable:

MasterID, Description

ChildTable

ChildID, MasterID, Description.

Using PIVOT / UNPIVOT how can i get result as below in single row.

if (MasterID : 1 got x child records)

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

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

笑叹一世浮沉 2024-09-06 03:56:36

这是一个 T_SQL,假设:

  • 您不知道结果中可能出现多少列。
  • 枢轴元素可能会有所不同(这就是第一个假设的原因)。
  • 您需要特定的顺序“ChildId1、ChilDesc1、ChildId2、ChildDesc2...asd”

DECLARE
@MaxCountOfChild int

-- 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

here is a T_SQL, assuming this:

  • You do not know how many columns may appear in the results.
  • The Pivot elements can vary (thats why the first assumption).
  • You need the specific order 'ChildId1, ChilDesc1, ChildId2, ChildDesc2... asd so ever'

DECLARE
@MaxCountOfChild int

-- 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

EDIT: The result is this:

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
大海や 2024-09-06 03:56:36

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

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 中执行此操作,而是使用报告工具或在中间层组件中构建结果集。

It greatly depends on whether the number of crosstabbed columns is fixed. If they are, then you can simply do something like:

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

There is also a way of achieving similar results use CASE functions.

However, if what you want is to have the number of crosstab columns determined at runtime, then the only means to do that inside of SQL Server is to use some fugly dynamic SQL. This is outside the realm of SQL Server's primary purpose which is to serve up data (as opposed to information). If you want a dynamic crosstab, I would recommend not doing it in SQL Server but instead use a reporting tool or build your result set in a middle-tier component.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文