我需要将一张表数据转换如下
不 | 姓名 | 位置 | 团体 |
---|---|---|---|
1 | 约翰 | 画家 | 印度 |
2 | 马克 | 垫圈 | 印度 |
3 | 彼得 | 军官 | 尼泊尔 |
4 | 天使 | 军官 | 印度 |
5 | 比彬 | 垫圈 | 尼泊尔 |
6 | 杰瑞 | 画家 | 尼泊尔 |
7 | 莉莉 | 画家 | 印度 |
8 | 乔治 | 垫圈 | 印度 |
9 | 亚伯拉罕 | 军官 | 尼泊尔 |
10 | 宝山 | 军官 | 印度 |
11 | 杰米 | 画家 | 尼泊尔 |
12 | 杰瑞 | 垫圈 | 尼泊尔 |
上表得到以下结果
不 | 画家 | 画家 | 垫圈 | 垫圈 | 军官 | 军官 |
---|---|---|---|---|---|---|
-- | 印度 | 尼泊尔 | 印度 | 尼泊尔 | 印度 | 尼泊尔 |
-- | -- | -- | -- | ---- | -- | ----- |
1 | 约翰 | 杰瑞 | 马克 | 比比 | 彼得 | 天使 |
2 | 莉莉 | 杰米 | 乔治 | 杰瑞 | 亚伯拉罕 | 宝山 |
每个位置下需要按组分配名字
create table #Temp
( SLno int,
Name varchar(20),
Postion varchar(20),
Groups Varchar(2))
INsert into #Temp Values (1 ,'John ','Painter ','India')
INsert into #Temp Values (2 ,'MArk ','Washer ','India')
INsert into #Temp Values (3 ,'Peter ','Officer ','Nepal')
INsert into #Temp Values (4 ,'Angel ','Officer ','India')
INsert into #Temp Values (5 ,'Bibin ','Washer ','Nepal')
INsert into #Temp Values (6 ,'Jerry ','Painter ','Nepal')
INsert into #Temp Values (7 ,'Lilly ','Painter ','India')
INsert into #Temp Values (8 ,'George',' Washer ','India')
INsert into #Temp Values (9 ,'Abrahm',' Officer ','Nepal')
INsert into #Temp Values (10,'Bosan',' Officer ','India')
INsert into #Temp Values (11,'Jemi ','Painter ','Nepal')
INsert into #Temp Values (12,'Jerry',' Washer ','Nepal')
DECLARE @Columns VARCHAR(MAX) = ''
SELECT @Columns = @Columns + ',' + QUOTENAME( Postion ) FROM (SELECT DISTINCT Postion FROM #Temp) T
SELECT @Columns = STUFF(@Columns, 1,1,'')
Print @Columns
DECLARE @SqlText VARCHAR(MAX) = 'SELECT * FROM #Temp
PIVOT ( (Groups) FOR Name IN ('+ @Columns +')) AS PVT'
SQL 查询不支持多行列标题,因此您必须选择 TM_A 和 SA_B 等列标题,并将最终的标题格式留给表示层。
这不是典型的简单 PIVOT 用例,因为:
为了解决第一个问题,我们可以使用
CONCAT()
构建一个组合值以在枢轴中使用。
对于第二个问题,我们需要为同一列中出现的每个值分配唯一的连续行号。这可以使用
ROW_NUMBER()
窗口函数和适当的 OVER()
子句来按目标列对数据进行分区并定义顺序来完成。
可以在 CTE 中计算组合的列键和行号,其中还包括将填充结果正文的
Name
列。其他列省略。此 CTE 现在的形式可以输入到 PIVOT
。
结果会是这样的:
WITH CTE_NumberedPositions AS (
SELECT
ROW_NUMBER() OVER(PARTITION BY Postion, Groups ORDER BY SLNo) AS No,
CONCAT(Postion, '_', Groups) AS Combined,
Name
FROM #Temp
)
SELECT
PVT.No,
PVT.TM_A, PVT.TM_B, PVT.SA_A, PVT.SA_B, PVT.DV_A, PVT.DV_B
FROM CTE_NumberedPositions
PIVOT (
MAX(Name)
FOR Combined IN (TM_A, TM_B, SA_A, SA_B, DV_A, DV_B)
) PVT
ORDER BY PVT.No
使用条件聚合也可以实现同样的效果,这样就无需预先计算组合列键。
WITH CTE_NumberedPositions AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Postion, Groups ORDER BY SLNo) AS No
FROM #Temp
)
SELECT
No,
MAX(CASE WHEN Postion = 'TM' AND Groups = 'A' THEN Name END) AS TM_A,
MAX(CASE WHEN Postion = 'TM' AND Groups = 'B' THEN Name END) AS TM_B,
MAX(CASE WHEN Postion = 'SA' AND Groups = 'A' THEN Name END) AS SA_A,
MAX(CASE WHEN Postion = 'SA' AND Groups = 'B' THEN Name END) AS SA_B,
MAX(CASE WHEN Postion = 'DV' AND Groups = 'A' THEN Name END) AS DV_A,
MAX(CASE WHEN Postion = 'DV' AND Groups = 'B' THEN Name END) AS DV_B
FROM CTE_NumberedPositions
GROUP BY No
ORDER BY No
如果您的
Postion
和 Groups
值事先未知,您将需要预先识别所有不同的 Postion
和 Groups
值,并使用 动态 SQL 生成并执行与上面。
示例结果(带有一些额外的测试数据):
没有 | TM_A | TM_B | SA_A | SA_B | DV_A | DV_B |
---|---|---|---|---|---|---|
1 | 约翰 | 杰瑞 | 马克 | 比彬 | 天使 | 彼得 |
2 | 约翰 | 杰瑞 | 马克 | 比彬 | 天使 | 彼得 |
3 | 拉里 | 休伊 | 卷毛 | 杜威 | 萌 | 路易 |
4 | 空 | 尼尔 | 空 | 迈克尔 | 空 | 嗡嗡声 |
请参阅 this db<>fiddle 进行演示。