如何在sql中将行转换为列

Posted

技术标签:

【中文标题】如何在sql中将行转换为列【英文标题】:How to convert rows into column in sql 【发布时间】:2016-08-29 08:39:20 【问题描述】:

我有一张桌子,

并需要将其转换为

我已经尝试过枢轴但无法弄清楚请帮助我。

【问题讨论】:

您是否有固定数量的要合并的行?否则,我建议将数据解析为数组 是的,我需要修改 ID 代码和员工姓名 @shakedzy 你没有回答这个问题。每个 ID 的行数是否固定 不,这不是固定的@shakedzy Convert Rows to columns using 'Pivot' in SQL Server的可能重复 【参考方案1】:

Pivot 不能处理多个列开箱即用有几种方法可以解决这个问题:

将此表变量用于所有测试,并请说明您的示例数据始终可复制“n”粘贴。最好的是一个 MCVE(最小完整可验证示例),您可以在其中设置一些类似我的代码。

DECLARE @tbl TABLE(ID INT, Code INT,EmployeeName VARCHAR(100),ExamName VARCHAR(100),Board VARCHAR(100),Result VARCHAR(100));
INSERT INTO @tbl VALUES
 (11537,12984,'TheName','SSC','b04','1st')
,(11537,12984,'TheName','HSC','b04','2nd')
,(11537,12984,'TheName','BA(H)','u33','2nd');

这是首先将您的数据连接到一列的代码。这允许PIVOT:

SELECT p.*
FROM
(
    SELECT tbl.ID
        ,tbl.Code
        ,tbl.EmployeeName
        ,'Exam_' + CAST(ROW_NUMBER() OVER(PARTITION BY tbl.ID ORDER BY tbl.Code) AS VARCHAR(100)) AS ColName
        ,ExamName + ' (' + Board + '): ' + Result AS Concatenated
    FROM @tbl AS tbl
) AS t
PIVOT
(
    MIN(Concatenated) FOR ColName IN(Exam_1,Exam_2,Exam_3 /*add as many as you need*/)
) AS p

结果:

11537   12984   TheName SSC (b04): 1st  HSC (b04): 2nd  BA(H) (u33): 2nd

下一个代码的作用完全一样,但创建的是 XML 而不是纯文本,这允许之后分离您的数据:

SELECT p.ID
      ,p.Code
      ,p.EmployeeName
      ,E1
      ,E1.value('(/exam/@ExamName)[1]','varchar(100)') AS ExamName1
      ,E1.value('(/exam/@Board)[1]','varchar(100)') AS Board1
      ,E1.value('(/exam/@Result)[1]','varchar(100)') AS Result1
      ,E2
      ,E2.value('(/exam/@ExamName)[1]','varchar(100)') AS ExamName2
      ,E2.value('(/exam/@Board)[1]','varchar(100)') AS Board2
      ,E2.value('(/exam/@Result)[1]','varchar(100)') AS Result2
      ,E3
      ,E3.value('(/exam/@ExamName)[1]','varchar(100)') AS ExamName3
      ,E3.value('(/exam/@Board)[1]','varchar(100)') AS Board3
      ,E3.value('(/exam/@Result)[1]','varchar(100)') AS Result3
FROM
(
    SELECT tbl.ID
        ,tbl.Code
        ,tbl.EmployeeName
        ,'Exam_' + CAST(ROW_NUMBER() OVER(PARTITION BY tbl.ID ORDER BY tbl.Code) AS VARCHAR(100)) AS ColName
        ,(SELECT ExamName AS [@ExamName],Board AS [@Board],Result AS [@Result] FOR XML PATH('exam')) AS AsXML
    FROM @tbl AS tbl
) AS t
PIVOT
(
    MIN(AsXML) FOR ColName IN(Exam_1,Exam_2,Exam_3 /*add as many as you need*/)
) AS p
OUTER APPLY
(
    SELECT CAST(p.Exam_1 AS XML) AS E1
          ,CAST(p.Exam_2 AS XML) AS E2
          ,CAST(p.Exam_3 AS XML) AS E3
) AS CastedToXml

结果:

11537   12984   TheName <exam ExamName="SSC" Board="b04" Result="1st" />    SSC b04 1st <exam ExamName="HSC" Board="b04" Result="2nd" />    HSC b04 2nd <exam ExamName="BA(H)" Board="u33" Result="2nd" />  BA(H)   u33 2nd

这是 old-fashioned-pivot,通常比正常的枢轴要好:

;WITH Numberd AS
(
    SELECT *
          ,ROW_NUMBER() OVER(PARTITION BY tbl.ID ORDER BY tbl.Code) AS Number
    FROM @tbl AS tbl
)
SELECT ID,Code,EmployeeName
      ,MAX(CASE WHEN Number=1 THEN ExamName END) AS ExamName1
      ,MAX(CASE WHEN Number=1 THEN Board END) AS Board1
      ,MAX(CASE WHEN Number=1 THEN Result END) AS Result1
      ,MAX(CASE WHEN Number=2 THEN ExamName END) AS ExamName2
      ,MAX(CASE WHEN Number=2 THEN Board END) AS Board2
      ,MAX(CASE WHEN Number=2 THEN Result END) AS Result2
      ,MAX(CASE WHEN Number=3 THEN ExamName END) AS ExamName3
      ,MAX(CASE WHEN Number=3 THEN Board END) AS Board3
      ,MAX(CASE WHEN Number=3 THEN Result END) AS Result3
FROM Numberd
GROUP BY ID,Code,EmployeeName

最后一个选项是动态 SQL...

【讨论】:

【参考方案2】:

谢谢大家,但是我的目的已经解决了

SELECT 
      he.Id,he.Code,he.Name EmployeeName,en.Name [ExamName],bu.Name        
      [Board],[Result] = CASE WHEN 
             ac.GPA IS NULL THEN ac.Result ELSE CAST(ac.GPA AS VARCHAR) END,
      ac.PassingYear 
INTO #Temp 
FROM H_Employee AS he 
INNER JOIN 
      H_AcademicQualification AS ac ON ac.H_EmployeeId = he.Id 
INNER JOIN 
      ExamName AS en ON en.Id = ac.ExamNameId 
INNER JOIN 
      GroupSubject AS gs ON gs.Id = ac.GroupSubjectId 
INNER JOIN 
      BoardUniversity AS bu ON bu.Id = ac.BoardUniversityId

然后

SELECT
     a.id, a.Code, a.EmployeeName, a.ExamName, a.Board, a.Result, a.Passingyear, 
     b.ExamName, b.Board, b.Result, b.Passingyear, 
     c.ExamName, c.Board, c.Result, c.Passingyear, 
     d.ExamName, d.Board, d.Result, d.Passingyear 
FROM 
    (SELECT 
          Id, Code, EmployeeName ,ExamName = CASE WHEN 
               ExamName LIKE 'S.S.%' THEN 'S.S.C' END,
          Board=Board ,[Result]=CASE WHEN 
               Result IS NULL THEN Result ELSE CAST(Result AS VARCHAR) END,
          Passingyear 
     FROM #temp 
     WHERE ExamName LIKE 'S.S.%') AS a 
LEFT JOIN 
        (SELECT Id, Code, EmployeeName ,ExamName = CASE WHEN 
            ExamName LIKE 'H.S.%' THEN 'H.S.C' END,
         Board=Board,[Result] = CASE WHEN 
            Result IS NULL THEN Result ELSE CAST(Result AS VARCHAR) END,
         Passingyear  
         FROM #temp 
         WHERE ExamName LIKE 'H.S.%') AS b 
     ON a.Id=b.Id 
LEFT JOIN 
        (SELECT Id, Code, EmployeeName ,ExamName = CASE WHEN 
             ExamName LIKE 'B.%' THEN 'Graduate' END, 
         Board=Board, [Result] = CASE WHEN 
             Result IS NULL THEN Result ELSE CAST(Result AS VARCHAR) END,
         Passingyear 
         FROM #temp 
         WHERE ExamName LIKE 'B.%') AS c 
     ON a.Id=c.Id 
LEFT JOIN 
        (SELECT Id, Code, EmployeeName, ExamName = CASE WHEN
             ExamName LIKE 'M.%' THEN 'Post-Graduate' END, 
         Board=Board,[Result] = CASE WHEN 
             Result IS NULL THEN Result ELSE CAST(Result AS VARCHAR) END,
         Passingyear 
         FROM #temp 
         WHERE ExamName LIKE 'M.%') AS d 
     ON a.Id=d.Id

以上查询为我服务,但会尝试@Shnugo Sample,无论如何谢谢大家。

【讨论】:

很高兴您自己找到了答案。如果您检查我的代码并且它对您有用,最好接受它作为答案,首先关闭此问题,其次帮助未来的访问者找到实际问题的答案,第三:这会为您和我的帐户:-)

以上是关于如何在sql中将行转换为列的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SQL Server 或 C# 代码中将行转换为列

在 SQL 中将行转换为列

如何在 SQL Server 中将行动态转换为列

在 Spark SQL (pyspark) 中将行转置为列

如何使用数据透视在 Oracle 中将行转换为列

在 Redshift 中将多行转换为列