来自多列的动态/条件枚举

时间:2017-08-28 19:06:09

标签: sql sql-server sql-server-2016

我正在尝试找出将多列枚举为两行的最佳方法。例如,下面的数据包含公司中每个职位的汇总员工数,按全职等效(FTE)状态划分。

+---------------+--------------+-------------------------+------+------+------+------+------+------+------+
|   Position    | PositionSlot | PositionSlotDescrpition |  PD  |  P5  |  P6  |  P7  |  P8  |  P9  |  FT  |
+---------------+--------------+-------------------------+------+------+------+------+------+------+------+
| 1-400400-0680 | NULL         | NULL                    | 7    | 1    | 2    | NULL | 1    | 18   | NULL |
| 1-400400-0041 | NULL         | NULL                    | NULL | NULL | NULL | NULL | NULL | NULL | 1    |
| 1-400400-0660 | NULL         | NULL                    | NULL | NULL | NULL | NULL | 1    | 6    | NULL |
+---------------+--------------+-------------------------+------+------+------+------+------+------+------+

因此,对于Position 1-400400-0680,上面列出了29名员工。

我需要枚举这些聚合,为每个PositionSlot创建一个顺序 Position,与FTE突破相关。

  • 每个Position的总行数应等于列PD, P5, P6, P7, P8, P9, and FT的总和。
  • PositionsSlotDescription应为原始列名PD, P5, P6, P7, P8, P9, and FT
  • PositionSlot应该是这些行的顺序编号。 广告位的排序无关紧要。因此PositionSlot = 1可以属于任何PositionsSlotDescription
  • 应重复PositionsSlotDescription / 根据原始聚合中的数字重复。

例如,Position 1-400400-0660的{​​{1}}为1,P8为6。因此,应该有1行P9 PositionSlotDescription和6行P8 PositionSlotDescriptionP9应该是1-7 < / p>

预期成果

PositionSlots

TEST SCRIPTS

+---------------+--------------+-------------------------+
|   Position    | PositionSlot | PositionSlotDescrpition |
+---------------+--------------+-------------------------+
| 1-400400-0041 |            1 | FT                      |

| 1-400400-0660 |            1 | P8                      |
| 1-400400-0660 |            2 | P9                      |
| 1-400400-0660 |            3 | P9                      |
| 1-400400-0660 |            4 | P9                      |
| 1-400400-0660 |            5 | P9                      |
| 1-400400-0660 |            6 | P9                      |
| 1-400400-0660 |            7 | P9                      |

| 1-400400-0680 |            1 | P5                      |
| 1-400400-0680 |            2 | P6                      |
| 1-400400-0680 |            3 | P6                      |
| 1-400400-0680 |            4 | P8                      |
| 1-400400-0680 |            5 | P9                      |
| 1-400400-0680 |            6 | P9                      |
| 1-400400-0680 |            7 | P9                      |
| 1-400400-0680 |            8 | P9                      |
| 1-400400-0680 |            9 | P9                      |
| 1-400400-0680 |           10 | P9                      |
| 1-400400-0680 |           11 | P9                      |
| 1-400400-0680 |           12 | P9                      |
| 1-400400-0680 |           13 | P9                      |
| 1-400400-0680 |           14 | P9                      |
| 1-400400-0680 |           15 | P9                      |
| 1-400400-0680 |           16 | P9                      |
| 1-400400-0680 |           17 | P9                      |
| 1-400400-0680 |           18 | P9                      |
| 1-400400-0680 |           19 | P9                      |
| 1-400400-0680 |           20 | P9                      |
| 1-400400-0680 |           21 | P9                      |
| 1-400400-0680 |           22 | P9                      |
| 1-400400-0680 |           23 | PD                      |
| 1-400400-0680 |           24 | PD                      |
| 1-400400-0680 |           25 | PD                      |
| 1-400400-0680 |           26 | PD                      |
| 1-400400-0680 |           27 | PD                      |
| 1-400400-0680 |           28 | PD                      |
| 1-400400-0680 |           29 | PD                      |
+---------------+--------------+-------------------------+

2 个答案:

答案 0 :(得分:3)

使用adhoc数字表和cross apply(values ...)来取消数据的显示:

;with numbers as (
  select top (32) --<-- 32 works for the example, increase for larger sets
      i=row_number() over(order by (select 1))
  from master..spt_values 
  order by i
)
select
    t.Position
  , PositionSlot=row_number() over (
      partition by t.Position 
      order by v.PositionSlotDescription, n.i
    )
  , v.PositionSlotDescription
from @table t
  cross apply (values 
    ('PD',PD),('P5',P5) ,('P6',P6) ,('P7',P7) ,('P8',P8) ,('P9',P9) ,('FT',FT) 
    ) v (PositionSlotDescription, Amount)
  inner join numbers n
    on n.i <= v.amount
where v.Amount is not null
order by t.Position, v.PositionSlotDescription

rextester演示:http://rextester.com/AMKCR70455

返回:

+---------------+--------------+-------------------------+
|   Position    | PositionSlot | PositionSlotDescription |
+---------------+--------------+-------------------------+
| 1-400400-0041 |            1 | FT                      |
| 1-400400-0660 |            1 | P8                      |
| 1-400400-0660 |            2 | P9                      |
| 1-400400-0660 |            3 | P9                      |
| 1-400400-0660 |            4 | P9                      |
| 1-400400-0660 |            5 | P9                      |
| 1-400400-0660 |            6 | P9                      |
| 1-400400-0660 |            7 | P9                      |
| 1-400400-0680 |            1 | P5                      |
| 1-400400-0680 |            2 | P6                      |
| 1-400400-0680 |            3 | P6                      |
| 1-400400-0680 |            4 | P8                      |
| 1-400400-0680 |            5 | P9                      |
| 1-400400-0680 |            6 | P9                      |
| 1-400400-0680 |            7 | P9                      |
| 1-400400-0680 |            8 | P9                      |
| 1-400400-0680 |            9 | P9                      |
| 1-400400-0680 |           10 | P9                      |
| 1-400400-0680 |           11 | P9                      |
| 1-400400-0680 |           12 | P9                      |
| 1-400400-0680 |           13 | P9                      |
| 1-400400-0680 |           14 | P9                      |
| 1-400400-0680 |           15 | P9                      |
| 1-400400-0680 |           16 | P9                      |
| 1-400400-0680 |           17 | P9                      |
| 1-400400-0680 |           18 | P9                      |
| 1-400400-0680 |           19 | P9                      |
| 1-400400-0680 |           20 | P9                      |
| 1-400400-0680 |           21 | P9                      |
| 1-400400-0680 |           22 | P9                      |
| 1-400400-0680 |           23 | PD                      |
| 1-400400-0680 |           24 | PD                      |
| 1-400400-0680 |           25 | PD                      |
| 1-400400-0680 |           26 | PD                      |
| 1-400400-0680 |           27 | PD                      |
| 1-400400-0680 |           28 | PD                      |
| 1-400400-0680 |           29 | PD                      |
+---------------+--------------+-------------------------+

参考:

<小时/> 对于较大的集合,您可以将cross join from master..spt_values添加到上面的number cte,或将numbers cte替换为此替代堆叠的cte:

;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, numbers as (
    select top(50000)
        i=row_number() over (order by (select 1))
    from n as deka cross join n as hecto cross join n as kilo
                   cross join n as tenK  cross join n as hundredK
)

rextester演示:http://rextester.com/OZZHR43374

答案 1 :(得分:0)

包含UNPIVOT和递归CTE

的选项
--Unpivot results
if object_id('tempdb..#unpivot') is not null drop table #unpivot
select
    u.Position
    ,u.Code
    ,u.Descr
into #unpivot
from
    @table t
unpivot
    (
    code
    for Descr in (PD, P5, P6, P7, P8, P9, FT)
    ) u


--Recursive CTE
;with cte as(
    select 
        t.Position
        ,Code = 1
        ,t.Descr
    from #unpivot t

    union all

    select 
        t2.Position
        ,cte.Code + 1
        ,t2.Descr
    from #unpivot t2
    inner join cte 
    on cte.Position = t2.Position 
    and cte.Descr = t2.Descr
    where t2.Code > cte.Code)

--Results
select 
    Position
    --,Code
    ,PositionSlotCode = row_number() over (partition by Position order by Descr) 
    ,Descr
from 
    cte
order by
    Position
    ,Descr
    ,Code