Sql 来自多个列的动态/条件枚举
我试图找出将多列枚举为两行的最佳方法。例如,以下数据包含公司每个职位的员工总数,按全职同等(FTE)状态细分 因此,对于Sql 来自多个列的动态/条件枚举,sql,sql-server,sql-server-2016,Sql,Sql Server,Sql Server 2016,我试图找出将多列枚举为两行的最佳方法。例如,以下数据包含公司每个职位的员工总数,按全职同等(FTE)状态细分 因此,对于职位1-400400-0680,有29名员工,如上所述 我需要列举这些集合,为每个职位创建一个与FTE分组相关的连续职位槽 每个位置的总行数应等于列PD、P5、P6、P7、P8、P9和FT的总和 PositionsSlotDescription应该是原始列名PD、P5、P6、P7、P8、P9和FT PositionSlot应该是这些行的顺序编号插槽的顺序无关紧要。因此Posi
职位1-400400-0680
,有29名员工,如上所述
我需要列举这些集合,为每个职位创建一个与FTE分组相关的连续职位槽
- 每个
位置的总行数应等于列
PD、P5、P6、P7、P8、P9和FT的总和李>
应该是原始列名PositionsSlotDescription
PD、P5、P6、P7、P8、P9和FT
应该是这些行的顺序编号插槽的顺序无关紧要。因此PositionSlot
可以属于任何PositionSlot=1
positionslotdescription
- 应重复
/ 根据原始聚合中的数字复制PositionsSlotDescription
位置1-400400-0660
有1个用于P8
,6个用于P9
。因此,应该有1行带有P8
的PositionSlotDescription
,6行带有PositionSlotDescription
的P9
,PositionSlots
应该是1-7
预期结果
+---------------+--------------+-------------------------+
| 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 |
+---------------+--------------+-------------------------+
测试脚本
declare @table table ( Position varchar(64) --UniqueIdentifier
,PositionSlot int
,PositionSlotDescrpition varchar(64)
,PD varchar(16)
,P5 varchar(16)
,P6 varchar(16)
,P7 varchar(16)
,P8 varchar(16)
,P9 varchar(16)
,FT varchar(16))
insert into @table
values
('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)
declare @expectedResults table( Position varchar(64)
,PositionSlot int
,PositionSlotDescrpition varchar(64))
insert into @expectedResults
values
('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')
使用临时数字表和交叉应用(值…)
取消对数据的预存:
;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
参考:
对于较大的集合,您可以将主..spt_值的交叉连接添加到上面的编号
cte中,或者用此备用堆叠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演示:一个带有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
Zim给人留下深刻印象的是,您花了很短的时间才弄明白这一点。@scsimon您包括了创建ddl和预期结果,这使它非常容易开始。这是一个有趣的问题,也很有帮助!哈,我同意!当我在这方面找不到任何东西时,我有点震惊,但也许我的问题/想法太复杂了。我有一个有效的解决方案,但它涉及到一个非PIVOT和递归CTE。我想我更喜欢你的。我要看看它是否能伸缩。我想会的。我稍后会发布我的,以供参考。@scsimon如果迭代次数很小,递归cte不应该是一个障碍;否则,我会猜测递归在比较中会表现不佳(类似于我的答案中引用的Aaron Bertrand文章中的基准测试结果)。当我根据生产数据测试时,我肯定会让您知道(我还没有回来)。我读过亚伦的那篇文章,还有他的大部分其他文章。你现在更快了,因为我必须取消Pivot(发布在下面)
;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
)
--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