码迷,mamicode.com
首页 > 数据库 > 详细

SQL SERVER 中的行列转换小结

时间:2016-07-21 19:54:23      阅读:298      评论:0      收藏:0      [点我收藏+]

标签:

1. 介绍说明

前段时间组内的小伙伴在升级维护项目中,经常设计一些复杂的数据转换问题,让我去看下有些地方怎么处理,我发现好多都是涉及到行列转换的问题,处理起来经常会比较麻烦,借此也总结一下,方便以后的查阅使用。该总结参照了网上的一些资料,也做了一些变动,如有更好的方法也欢迎指出。

演示的脚本见 3.测试数据脚本

2. 例子演示

2.1 实现行转列

(1) Case WHEN 实现行转列 

技术分享
/*-----1.1 Case WHEN 实现行转列----------*/

--(1)静态SQL
SELECT [姓名],
 max(CASE 课程 WHEN 语文 THEN 分数 ELSE 0 end) AS 语文,
 max(CASE 课程 WHEN 数学 THEN 分数 ELSE 0 end)AS 数学,
 max(CASE 课程 WHEN 物理 THEN 分数 ELSE 0 end)AS 物理,
 SUM(分数) AS 总分,
 AVG(分数) AS 平均分
FROM tbScore GROUP BY [姓名]

--(2)动态SQL
DECLARE @sql VARCHAR(500)
SET @sql = SELECT [姓名]
SELECT  @sql = @sql + ,MAX(CASE [课程] WHEN ‘‘‘ + [课程] + ‘‘‘ THEN [分数] ELSE 0 END)[ + [课程] + ]
FROM    ( 
            SELECT DISTINCT [课程] FROM tbScore
        ) T1
--同FROM tbScore  GROUP BY [课程],默认按课程名排序
SET @sql = @sql +  FROM tbScore GROUP BY [姓名]
PRINT @sql:  + @sql
EXEC(@sql)
View Code

 技术分享

 

 (2) PIVOT 实现行转列,其中的NULL值发现还不好处理为0

技术分享
--(1)静态SQL
SELECT  [姓名] ,
        [语文] ,
        [数学] ,
        [物理]
FROM    ( SELECT    [分数] ,
                    [课程] ,
                    [姓名]
          FROM      tbScore
        ) AS SourceTable PIVOT ( AVG([分数]) FOR [课程] IN ( 语文, 数学, 物理 ) ) T


--(2)动态SQL
DECLARE @sql2 VARCHAR(8000)
SET @sql2 = ‘‘
SELECT @sql2 = @sql2 + , + [课程] FROM dbo.tbScore GROUP BY [课程]
--STUFF: 删除指定长度的字符,并在指定的起点处插入另一组字符。
SET @sql2= STUFF(@sql2,1,1,‘‘)  --去掉首个‘,‘
SET @sql2 = SELECT [姓名], + @sql2 +  FROM (SELECT [分数],[课程],[姓名] FROM tbScore ) AS SourceTable PIVOT ( AVG([分数]) FOR [课程] IN (  + @sql2 + ) ) T
PRINT @sql2
EXEC(@sql2)
View Code

技术分享

 

2.1 实现转行

 (1) UNION 实现列转行

技术分享
--(1)静态SQL
SELECT * FROM (
    SELECT [姓名],语文 AS 课程,[语文] AS 分数 ,[日期] FROM tbScoreNew
    UNION ALL
    SELECT [姓名],数学 AS 课程,[数学] AS 分数 ,[日期] FROM tbScoreNew
    UNION ALL
    SELECT [姓名],物理 AS 课程,[物理] AS 分数 ,[日期] FROM tbScoreNew
) T ORDER BY [姓名]

--(2)动态SQL
DECLARE @sql3 VARCHAR(8000)
SELECT @sql3 = ISNULL(@sql3 +  UNION ALL ,‘‘) +  SELECT [姓名], + QUOTENAME(name,‘‘‘‘) +  AS 课程, + QUOTENAME(name) + ,[日期] FROM tbScoreNew
FROM sys.columns 
WHERE object_id = OBJECT_ID(tbScoreNew) AND  name NOT IN (姓名,日期)
SET @sql3 = SELECT * FROM (  + @sql3  +  ) T ORDER BY [姓名]
PRINT @sql3
EXEC (@sql3)
View Code

技术分享

 

 (2) UNPIVOT 实现列转行

技术分享
--(1)静态SQL
SELECT * FROM (
    SELECT [姓名],[日期],[语文],[数学],[物理] FROM dbo.tbScoreNew
) T UNPIVOT ([分数] FOR [课程] IN ([语文],[数学],[物理])) T2
ORDER BY [姓名]


--(2)动态SQL
DECLARE @sql4 VARCHAR(8000)
SELECT @sql4 = ISNULL(@sql4 + ,,‘‘) + QUOTENAME(name)
FROM sys.columns 
WHERE object_id = OBJECT_ID(tbScoreNew) AND  name NOT IN (姓名,日期)
SET @sql4 = SELECT * FROM ( SELECT [姓名],[日期], + @sql4 +  FROM dbo.tbScoreNew ) T UNPIVOT ([分数] FOR [课程] IN (+ @sql4 +)) T2 ORDER BY [姓名]
PRINT @sql4
EXEC (@sql4)
View Code

技术分享

 

2.3 动态增加列实现行转列 

这个参照部门小伙伴的项目上的要求写的一个例子, 由于涉及的转换列同时有多个字段,用上面的行列转换处理起来都很不方便,所以采用比较普通的动态增加列的方式处理

测试数据脚本为附件脚本中的 “3.动态增加列实现行转列" 脚本

要求: 将【部门预算】、【实际预算】、【剩余预算】按照年份横向统计显示,且统计数据按部门、项目分组显示

技术分享
CREATE TABLE #tmpYear
(
    [YEAR] INT,
    ID INT IDENTITY
)

--保存最终结果
CREATE TABLE #tmpResult
(
    ID INT IDENTITY,
    DeptCode VARCHAR(20),--部门编码
    DeptName NVARCHAR(100), --部门名称
    ProCode VARCHAR(20),--项目编码
    ProName NVARCHAR(100),--项目名称
    KeyCode VARCHAR(50)
)
GO

--1.写入分组数据
INSERT INTO #tmpResult( DeptCode ,DeptName , ProCode ,ProName,KeyCode)
SELECT DeptCode,MAX(DeptName), ProCode,MAX(ProName),DeptCode + _ + ProCode FROM tbDeptBudget GROUP BY DeptCode,ProCode

--2.计算预算结果数据
--写入年份数据
INSERT INTO #tmpYear SELECT DISTINCT Year FROM dbo.tbDeptBudget

DECLARE @SQL VARCHAR(5000)
DECLARE @ColName1 VARCHAR(50)
DECLARE @ColName2 VARCHAR(50)
DECLARE @ColName3 VARCHAR(50)
DECLARE @Year INT
DECLARE @ID INT
DECLARE @RowNum INT
SET @Year = 0
SET @ID = 1
SET @RowNum = (SELECT COUNT(0) FROM #tmpYear)
WHILE @ID <= @RowNum
BEGIN
    SET @Year = (SELECT [YEAR] FROM #tmpYear WHERE ID = @ID)    
    SET @ColName1 = Bduget_ + CAST(@Year AS VARCHAR(10))
    SET @ColName2 = Fact_ + CAST(@Year AS VARCHAR(10))
    SET @ColName3 = Remain_ + CAST(@Year AS VARCHAR(10))
    
    --增加动态列
    SET @SQL = ALTER TABLE #tmpResult ADD  + @ColName1 +  Decimal(18,2)
              + ALTER TABLE #tmpResult ADD  + @ColName2 +  Decimal(18,2)
              + ALTER TABLE #tmpResult ADD  + @ColName3 +  Decimal(18,2)
    EXEC(@SQL)
    
    --写入动态列数据
    SET @SQL = UPDATE T SET  + @ColName1 +  = S.BudgetAmount, + @ColName2 +  = S.FactAmount,+ @ColName3 +  = S.RemainAmount 
        +  FROM #tmpResult T INNER JOIN ( 
        +  SELECT (DeptCode +  + QUOTENAME(_,‘‘‘‘) + + ProCode) AS KeyCode,MAX(BudgetAmount)AS BudgetAmount ,MAX(FactAmount)AS FactAmount,MAX(RemainAmount)AS RemainAmount 
        +  FROM dbo.tbDeptBudget WHERE Year=  + CAST (@Year AS VARCHAR(10))
        +  GROUP BY DeptCode,ProCode 
        + ) S ON T.KeyCode = S.KeyCode 
    
    PRINT @SQL
    EXEC(@SQL)
        
    SET @ID = @ID  + 1
END

--3.返回结果
SELECT * FROM #tmpResult

--4.清理临时表
IF OBJECT_ID(tempdb..#tmpYear) IS NOT NULL
BEGIN
    DROP TABLE #tmpYear
END
IF OBJECT_ID(tempdb..#tmpResult) IS NOT NULL
BEGIN
    DROP TABLE #tmpResult
END
View Code

 技术分享

 

3. 测试数据脚本

技术分享
/*-----1.行转列的测试数据--------------------------*/
IF OBJECT_ID(tbScore) IS NOT NULL 
    DROP TABLE tbScore

GO

CREATE TABLE tbScore
    (
      姓名 VARCHAR(10) ,
      课程 VARCHAR(10) ,
      分数 INT,
      日期 DATETIME
    )
GO

INSERT  INTO tbScore VALUES  ( 张三, 语文, 74,GETDATE() )
--INSERT  INTO tbScore VALUES  ( ‘张三‘, ‘数学‘, 83 ,GETDATE() )
INSERT  INTO tbScore VALUES  ( 张三, 物理, 93 ,GETDATE() )
INSERT  INTO tbScore VALUES  ( 李四, 语文, 74 ,GETDATE() )
INSERT  INTO tbScore VALUES  ( 李四, 数学, 84 ,GETDATE() )
INSERT  INTO tbScore VALUES  ( 李四, 物理, 94 ,GETDATE() )
GO

/*-----2.列转行的测试数据--------------------------*/
IF OBJECT_ID(tbScoreNew) IS NOT NULL 
    DROP TABLE tbScoreNew

GO

CREATE TABLE tbScoreNew(
      姓名 VARCHAR(10) ,
      语文 INT,
      数学 INT,
      物理 INT,
      日期 DATETIME
    )
GO

INSERT  INTO tbScoreNew VALUES  ( 李四, 74,84,94,GETDATE() )
INSERT  INTO tbScoreNew VALUES  ( 张三, 74,83,93,GETDATE() )
GO


/*-----3.动态增加列实现行转列(模拟组内项目要求)--------------------------*/
IF OBJECT_ID(tbDeptBudget) IS NOT NULL 
    DROP TABLE tbDeptBudget

GO
--部门预算
CREATE TABLE tbDeptBudget
(
    ID INT IDENTITY(1,1) PRIMARY KEY,
    DeptCode VARCHAR(20),--部门编码
    DeptName NVARCHAR(100), --部门名称
    ProCode VARCHAR(20),--项目编码
    ProName NVARCHAR(100),--项目名称
    Year INT, --年度
    BudgetAmount DECIMAL(18,2), --预算金额
    FactAmount DECIMAL(18,2), --实际金额
    RemainAmount DECIMAL(18,2), --剩余金额
    CreateTime DATETIME  --创建时间
)
GO

INSERT INTO tbDeptBudget(DeptName,DeptCode,ProCode,ProName,YEAR,BudgetAmount,FactAmount,RemainAmount,CreateTime)
VALUES(人事部,010000,01,差旅费,2014,100000.00,80000.00,20000.00,GETDATE());
INSERT INTO tbDeptBudget(DeptName,DeptCode,ProCode,ProName,YEAR,BudgetAmount,FactAmount,RemainAmount,CreateTime)
VALUES(人事部,010000,01,差旅费,2015,110000.00,90000.00,50000.00,GETDATE());
INSERT INTO tbDeptBudget(DeptName,DeptCode,ProCode,ProName,YEAR,BudgetAmount,FactAmount,RemainAmount,CreateTime)
VALUES(人事部,010000,01,差旅费,2016,120000.00,100000.00,80000.00,GETDATE());
INSERT INTO tbDeptBudget(DeptName,DeptCode,ProCode,ProName,YEAR,BudgetAmount,FactAmount,RemainAmount,CreateTime)
VALUES(人事部,010000,02,办公用品,2015,200000.00,150000.00,10000.00,GETDATE());
INSERT INTO tbDeptBudget(DeptName,DeptCode,ProCode,ProName,YEAR,BudgetAmount,FactAmount,RemainAmount,CreateTime)
VALUES(人事部,010000,02,办公用品,2016,160000.00,120000.00,80000.00,GETDATE());
INSERT INTO tbDeptBudget(DeptName,DeptCode,ProCode,ProName,YEAR,BudgetAmount,FactAmount,RemainAmount,CreateTime)
VALUES(财务部,020000,02,办公用品,2014,50000.00,40000.00,0.00,GETDATE());
INSERT INTO tbDeptBudget(DeptName,DeptCode,ProCode,ProName,YEAR,BudgetAmount,FactAmount,RemainAmount,CreateTime)
VALUES(财务部,020000,02,办公用品,2015,50000.00,50000.00,10000.00,GETDATE());
INSERT INTO tbDeptBudget(DeptName,DeptCode,ProCode,ProName,YEAR,BudgetAmount,FactAmount,RemainAmount,CreateTime)
VALUES(财务部,020000,02,办公用品,2016,60000.00,50000.00,40000.00,GETDATE());
INSERT INTO tbDeptBudget(DeptName,DeptCode,ProCode,ProName,YEAR,BudgetAmount,FactAmount,RemainAmount,CreateTime)
VALUES(财务部,020000,03,采购费,2016,100000.00,80000.00,60000.00,GETDATE());
View Code

 

测试脚本附件

4. 参考资料

 http://www.cnblogs.com/zhangzt/archive/2010/07/29/1787825.html  

 http://www.cnblogs.com/gaizai/p/3753296.html

 

SQL SERVER 中的行列转换小结

标签:

原文地址:http://www.cnblogs.com/johden2/p/5692765.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!