透视表以动态创建没有行聚合的新列

时间:2017-05-19 19:38:49

标签: sql sql-server tsql pivot

我有一个名为[DocType]的表,列出了各种文档类型以及层级父子关联。我想正确PIVOT从四列到N个列的数据组。亲子关联的数量不是静态的,我需要考虑分类级别可以达到N个数的情况。

以下是一组数据样本。 Id是固定密钥。 CatFolderLevel是层次结构级别 - 值1表示父级,2表示子级,3表示子级,依此类推。 CatParentId与其父Id有关。

╔═════╤═════════════════════════════╤════════════════╤═════════════╗
║ Id  │ CatName                     │ CatFolderLevel │ CatParentId ║
╠═════╪═════════════════════════════╪════════════════╪═════════════╣
║ 66  │ Grades                      │ 1              │ 0           ║
╟─────┼─────────────────────────────┼────────────────┼─────────────╢
║ 68  │ Transcript                  │ 2              │ 66          ║
╟─────┼─────────────────────────────┼────────────────┼─────────────╢
║ 129 │ Reports                     │ 1              │ 0           ║
╟─────┼─────────────────────────────┼────────────────┼─────────────╢
║ 137 │ Evaluation Summary          │ 2              │ 129         ║
╟─────┼─────────────────────────────┼────────────────┼─────────────╢
║ 317 │ Student Services            │ 1              │ 0           ║
╟─────┼─────────────────────────────┼────────────────┼─────────────╢
║ 333 │ Programs - Student Services │ 2              │ 317         ║
╟─────┼─────────────────────────────┼────────────────┼─────────────╢
║ 340 │ Nursing Services            │ 3              │ 333         ║
╟─────┼─────────────────────────────┼────────────────┼─────────────╢
║ 352 │ Fine Arts                   │ 1              │ 0           ║
╟─────┼─────────────────────────────┼────────────────┼─────────────╢
║ 357 │ Budget - Fine Arts          │ 2              │ 352         ║
╟─────┼─────────────────────────────┼────────────────┼─────────────╢
║ 358 │ Operational Budget          │ 3              │ 357         ║
╚═════╧═════════════════════════════╧════════════════╧═════════════╝

所需的输出将如下所示:

╔══════════╤══════════════════╤══════════════════════╤═══════════════════╤══════════╤═════════════════════════════╤══════════════════════╤═══════════════════╤══════════╤════════════════════╤══════════════════════╤═══════════════════╗
║ Level1ID │ Level1CatName    │ Level1CatFolderLevel │ Level1CatParentID │ Level2ID │ Level2CatName               │ Level2CatFolderLevel │ Level2CatParentID │ Level3ID │ Level3CatName      │ Level3CatFolderLevel │ Level3CatParentID ║
╠══════════╪══════════════════╪══════════════════════╪═══════════════════╪══════════╪═════════════════════════════╪══════════════════════╪═══════════════════╪══════════╪════════════════════╪══════════════════════╪═══════════════════╣
║ 317      │ Student Services │ 1                    │ 0                 │ 333      │ Programs - Student Services │ 2                    │ 317               │ 340      │ Nursing Services   │ 3                    │ 333               ║
╟──────────┼──────────────────┼──────────────────────┼───────────────────┼──────────┼─────────────────────────────┼──────────────────────┼───────────────────┼──────────┼────────────────────┼──────────────────────┼───────────────────╢
║ 352      │ Fine Arts        │ 1                    │ 0                 │ 357      │ Budget - Fine Arts          │ 2                    │ 352               │ 358      │ Operational Budget │ 3                    │ 357               ║
╟──────────┼──────────────────┼──────────────────────┼───────────────────┼──────────┼─────────────────────────────┼──────────────────────┼───────────────────┼──────────┼────────────────────┼──────────────────────┼───────────────────╢
║ 66       │ Grades           │ 1                    │ 0                 │ 68       │ Transcript                  │ 2                    │ 66                │ NULL     │ NULL               │ NULL                 │ NULL              ║
╟──────────┼──────────────────┼──────────────────────┼───────────────────┼──────────┼─────────────────────────────┼──────────────────────┼───────────────────┼──────────┼────────────────────┼──────────────────────┼───────────────────╢
║ 129      │ Reports          │ 1                    │ 0                 │ 137      │ Evaluation Summary          │ 2                    │ 129               │ NULL     │ NULL               │ NULL                 │ NULL              ║
╚══════════╧══════════════════╧══════════════════════╧═══════════════════╧══════════╧═════════════════════════════╧══════════════════════╧═══════════════════╧══════════╧════════════════════╧══════════════════════╧═══════════════════╝

我用来制作上述样本集的方法是使用CTE(见下文),但它是静态的,坦白地说,对我的尝试感到不满,非常简陋和垃圾。

;WITH Level3 AS(SELECT
    Id
    ,CatName
    ,CatFolderLevel
    ,CatParentId
FROM
    [DocType]
WHERE
    CatFolderLevel = 3)
,Level2 AS (SELECT
    Id
    ,CatName
    ,CatFolderLevel
    ,CatParentId
FROM
    [DocType]
WHERE
    CatFolderLevel = 2)
,Level1 AS (SELECT
    Id
    ,CatName
    ,CatFolderLevel
    ,CatParentId
FROM
    [DocType]
WHERE
    CatFolderLevel = 1)
SELECT
    Level1.Id AS 'Level1ID'
    ,Level1.CatName AS 'Level1CatName'
    ,Level1.CatFolderLevel 'Level1CatFolderLevel'
    ,Level1.CatParentId 'Level1CatParentID'
    ,Level2.Id AS 'Level2ID'
    ,Level2.CatName AS 'Level2CatName'
    ,Level2.CatFolderLevel AS 'Level2CatFolderLevel'
    ,Level2.CatParentId AS 'Level2CatParentID'
    ,Level3.Id AS 'Level3ID'
    ,Level3.CatName AS 'Level3CatName'
    ,Level3.CatFolderLevel AS 'Level3CatFolderLevel'
    ,Level3.CatParentId AS 'Level3CatParentID'
FROM
    Level3
    JOIN Level2
       ON Level3.CatParentId = Level2.Id
    JOIN Level1
       ON Level2.CatParentId = Level1.Id
UNION ALL
SELECT
    Level1.Id AS 'Level1ID'
    ,Level1.CatName AS 'Level1CatName'
    ,Level1.CatFolderLevel 'Level1CatFolderLevel'
    ,Level1.CatParentId 'Level1CatParentID'
    ,Level2.Id AS 'Level2ID'
    ,Level2.CatName AS 'Level2CatName'
    ,Level2.CatFolderLevel AS 'Level2CatFolderLevel'
    ,Level2.CatParentId AS 'Level2CatParentID'
    ,NULL AS 'Level3ID'
    ,NULL AS 'Level3CatName'
    ,NULL AS 'Level3CatFolderLevel'
    ,NULL AS 'Level3CatParentID'
FROM
    Level2
    JOIN Level1
       ON Level2.CatParentId = Level1.Id

我已经使用动态T-SQL探索了PIVOT的各种用法,但它们都聚合了数据,我不确定如何根据需要动态地正确转换数据而不进行聚合。

有人能指出我正确的方向来产生我想要的结果吗?

3 个答案:

答案 0 :(得分:1)

这不是真正的PIVOT。这是一组动态的JOINS。

我将构建一个动态SQL查询,其中我连接N个派生表,其中每个派生表都是CatFolderLevel。每个级别在ID / ParentId配对上连接到它上面的级别。当然,使用OUTER连接,这样你仍然可以得到没有孩子的父母。

答案 1 :(得分:1)

问题是你使用的是union而不是left join。您的查询(在CTE部分之后)可以重写为

SELECT 
  Level1.Id AS 'Level1ID',
  Level1.CatName AS 'Level1CatName',
  Level1.CatFolderLevel 'Level1CatFolderLevel',
  Level1.CatParentId 'Level1CatParentID',

  Level2.Id AS 'Level2ID',
  Level2.CatName AS 'Level2CatName',
  Level2.CatFolderLevel AS 'Level2CatFolderLevel',
  Level2.CatParentId AS 'Level2CatParentID',

  Level3.Id AS 'Level3ID',
  Level3.CatName AS 'Level3CatName',
  Level3.CatFolderLevel AS 'Level3CatFolderLevel',
  Level3.CatParentId AS 'Level3CatParentID'
FROM Level1
LEFT JOIN Level2 ON Level2.CatParentId = Level1.Id AND Level2.CatFolderLevel = 2
LEFT JOIN Level3 ON Level3.CatParentId = Level2.Id AND Level3.CatFolderLevel = 3

鉴于此,应该很容易看到如何将此动态变为N级:

SELECT 
  Level1.Id AS 'Level1ID',
  Level1.CatName AS 'Level1CatName',
  Level1.CatFolderLevel 'Level1CatFolderLevel',
  Level1.CatParentId 'Level1CatParentID',

  -- repeat as needed 
  Level{x}.Id AS 'Level{x}ID',
  Level{x}.CatName AS 'Level{x}CatName',
  Level{x}.CatFolderLevel AS 'Level{x}CatFolderLevel',
  Level{x}.CatParentId AS 'Level{x}CatParentID',

FROM Level1
-- repeat as needed
LEFT JOIN Level{x} ON Level{x}.CatParentId = Level{x-1}.Id AND Level{x}.CatFolderLevel = {x}

答案 2 :(得分:0)

您可以按如下方式生成左连接:您需要提供表名,或者可以相应地更改脚本。我计算表以避免循环。

declare @Query nvarchar(max);
declare @yourtablename nvarchar(50) = '#yourfolder ';

SET @query = 'Select '
;with c1 as  (  select * from (values (1), (1),(1), (1),(1), (1),(1), (1),(1), (1)) v(n)    )--tally table
, c2 as  (  select n1.* from c1 n1, c1 n2, c1 n3, c1 n4 ) 
, cte1 as ( select top(select max(catfolderlevel) m from #yourfolder) RowN = Row_number() over(order by (select null)) from c2 )
select @Query += concat(' l', RowN,'.Id as Level',RowN,'Id,  l',RowN,'.CatName as Level', RowN, 'CatName, l', RowN, '.CatFolderLevel as Level', RowN, 'CatFolder, l', RowN, '.CatParentId as Level', RowN,'CatParentId , ') from cte1

SELECT @Query = left(@query,len(@Query)-2)

Select @query += '  from ' + @yourtablename + ' l1  '
;with c1 as  (  select * from (values (1), (1),(1), (1),(1), (1),(1), (1),(1), (1)) v(n)    )
, c2 as  (  select n1.* from c1 n1, c1 n2, c1 n3, c1 n4 ) 
, cte1 as ( select top(select max(catfolderlevel)-1 m from #yourfolder) RowN = Row_number() over(order by (select null))+1 from c2 )
Select @Query += concat(' left join #yourfolder l',RowN, ' on l', RowN-1,'.id = l', RowN,'.catparentid and l', RowN-1,'.catfolderlevel = ', RowN -1) from cte1

select @Query += ' Where l1.CatFolderLevel = 1'

select @Query  --Check your query
exec sp_executesql @Query

输出:

+----------+------------------+-----------------+-------------------+----------+---------------+-----------------+-----------------------------+----------+--------------------+--------------------+-------------------+---+-----+-----+--------------------+------+------+------+------+------+
| Level1Id |  Level1CatName   | Level1CatFolder | Level1CatParentId | Level2Id | Level2CatName | Level2CatFolder |      Level2CatParentId      | Level3Id |   Level3CatName    |  Level3CatFolder   | Level3CatParentId |   |     |     |                    |      |      |      |      |      |
+----------+------------------+-----------------+-------------------+----------+---------------+-----------------+-----------------------------+----------+--------------------+--------------------+-------------------+---+-----+-----+--------------------+------+------+------+------+------+
|       66 | Grades           |                 |                   |          |               |                 | 1                           |        0 | 68                 | Transcript         |                   |   |     |     | 2                  | 66   | NULL | NULL | NULL | NULL |
|      129 | Reports          |                 |                   |          |               |                 | 1                           |        0 | 137                | Evaluation Summary |                   |   |   2 | 129 | NULL               | NULL | NULL | NULL |      |      |
|      317 | Student Services |                 |                   |        1 |             0 |             333 | Programs - Student Services |        2 | 317                | 340                | Nursing Services  |   |     |   3 | 333                |      |      |      |      |      |
|      352 | Fine Arts        |                 |                   |          |               |               1 | 0                           |      357 | Budget - Fine Arts |                    |                   | 2 | 352 | 358 | Operational Budget |      |      | 3    | 357  |      |
+----------+------------------+-----------------+-------------------+----------+---------------+-----------------+-----------------------------+----------+--------------------+--------------------+-------------------+---+-----+-----+--------------------+------+------+------+------+------+

为您的参考生成查询:

Select  l1.Id as Level1Id,  l1.CatName as Level1CatName, l1.CatFolderLevel as Level1CatFolder, l1.CatParentId as Level1CatParentId ,  l2.Id as Level2Id,  l2.CatName as Level2CatName, l2.CatFolderLevel as Level2CatFolder, l2.CatParentId as Level2CatParentId ,  l3.Id as Level3Id,  l3.CatName as Level3CatName, l3.CatFolderLevel as Level3CatFolder, l3.CatParentId as Level3CatParentId from #yourfolder  l1  left join #yourfolder l2 on l1.id = l2.catparentid and l1.catfolderlevel = 1 left join #yourfolder l3 on l2.id = l3.catparentid and l2.catfolderlevel = 2 Where l1.CatFolderLevel = 1