如何使用CTE通过组来转换一个表值

问题描述 投票:0回答:1

我需要将一张表数据转换如下

姓名 位置 团体
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 sql-server
1个回答
0
投票

SQL 查询不支持多行列标题,因此您必须选择 TM_A 和 SA_B 等列标题,并将最终的标题格式留给表示层。

这不是典型的简单 PIVOT 用例,因为:

  1. 结果列代表两个源列的组合,
  2. 源不包含行分组值。

为了解决第一个问题,我们可以使用

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 进行演示。

© www.soinside.com 2019 - 2024. All rights reserved.