原文地址:http://www.cnblogs.com/kerrycode/archive/2010/07/28/1786547.html

行转列,列转行是我们在开发过程中经常碰到的问题。行转列一般通过CASE WHEN 语句来实现,也可以通过 SQL SERVER 2005 新增的运算符PIVOT来实现。用传统的方法,比较好理解。层次清晰,而且比较习惯。 但是PIVOT 、UNPIVOT提供的语法比一系列复杂的SELECT...CASE 语句中所指定的语法更简单、更具可读性。下面我们通过几个简单的例子来介绍一下列转行、行转列问题。

我们首先先通过一个老生常谈的例子,学生成绩表(下面简化了些)来形象了解下行转列

CREATE  TABLE [StudentScores]
(
   [UserName]         NVARCHAR(20),        --学生姓名
    [Subject]          NVARCHAR(30),        --科目
    [Score]            FLOAT,               --成绩
)
 
INSERT INTO [StudentScores] SELECT 'Nick', '语文', 80
 
INSERT INTO [StudentScores] SELECT 'Nick', '数学', 90
 
INSERT INTO [StudentScores] SELECT 'Nick', '英语', 70
 
INSERT INTO [StudentScores] SELECT 'Nick', '生物', 85
 
INSERT INTO [StudentScores] SELECT 'Kent', '语文', 80
 
INSERT INTO [StudentScores] SELECT 'Kent', '数学', 90
 
INSERT INTO [StudentScores] SELECT 'Kent', '英语', 70
 
INSERT INTO [StudentScores] SELECT 'Kent', '生物', 85

如果我想知道每位学生的每科成绩,而且每个学生的全部成绩排成一行,这样方便我查看、统计,导出数据

SELECT 
      UserName, 
      MAX(CASE Subject WHEN '语文' THEN Score ELSE 0 END) AS '语文',
      MAX(CASE Subject WHEN '数学' THEN Score ELSE 0 END) AS '数学',
      MAX(CASE Subject WHEN '英语' THEN Score ELSE 0 END) AS '英语',
      MAX(CASE Subject WHEN '生物' THEN Score ELSE 0 END) AS '生物'
FROM dbo.[StudentScores]
GROUP BY UserName
 
查询结果如图所示,这样我们就能很清楚的了解每位学生所有的成绩了

接下来我们来看看第二个小列子。有一个游戏玩家充值表(仅仅为了说明,举的一个小例子),

CREATE TABLE [Inpours]
(
   [ID]                INT IDENTITY(1,1), 
   [UserName]          NVARCHAR(20),  --游戏玩家
    [CreateTime]        DATETIME,      --充值时间    
    [PayType]           NVARCHAR(20),  --充值类型    
    [Money]             DECIMAL,       --充值金额
    [IsSuccess]         BIT,           --是否成功 1表示成功, 0表示失败
    CONSTRAINT [PK_Inpours_ID] PRIMARY KEY(ID)
)
 
INSERT INTO Inpours SELECT '张三', '2010-05-01', '支付宝', 50, 1
 
INSERT INTO Inpours SELECT '张三', '2010-06-14', '支付宝', 50, 1
 
INSERT INTO Inpours SELECT '张三', '2010-06-14', '手机短信', 100, 1
 
INSERT INTO Inpours SELECT '李四', '2010-06-14', '手机短信', 100, 1
 
INSERT INTO Inpours SELECT '李四', '2010-07-14', '支付宝', 100, 1
 
INSERT INTO Inpours SELECT '王五', '2010-07-14', '工商银行卡', 100, 1
 
INSERT INTO Inpours SELECT '赵六', '2010-07-14', '建设银行卡', 100, 1
下面来了一个统计数据的需求,要求按日期、支付方式来统计充值金额信息。这也是一个典型的行转列的例子。我们可以通过下面的脚本来达到目的
代码
SELECT CONVERT(VARCHAR(10), CreateTime, 120) AS CreateTime, 
       CASE PayType WHEN '支付宝'     THEN SUM(Money) ELSE 0 END AS '支付宝', 
       CASE PayType WHEN '手机短信'    THEN SUM(Money) ELSE 0 END AS '手机短信', 
       CASE PayType WHEN '工商银行卡'  THEN SUM(Money) ELSE 0 END AS '工商银行卡', 
       CASE PayType WHEN '建设银行卡'  THEN SUM(Money) ELSE 0 END AS '建设银行卡' 
FROM Inpours 
GROUP BY CreateTime, PayType

如图所示,我们这样只是得到了这样的输出结果,还需进一步处理,才能得到想要的结果

SELECT 
       CreateTime, 
       ISNULL(SUM([支付宝])    , 0)  AS [支付宝]    , 
       ISNULL(SUM([手机短信])  , 0)  AS [手机短信]   , 
       ISNULL(SUM([工商银行卡]), 0)  AS [工商银行卡] ,  
       ISNULL(SUM([建设银行卡]), 0)  AS [建设银行卡]
FROM
(
    SELECT CONVERT(VARCHAR(10), CreateTime, 120) AS CreateTime,
           CASE PayType WHEN '支付宝'     THEN SUM(Money) ELSE 0 END AS '支付宝' ,
           CASE PayType WHEN '手机短信'   THEN SUM(Money) ELSE 0 END AS '手机短信',
           CASE PayType WHEN '工商银行卡' THEN SUM(Money) ELSE 0 END AS '工商银行卡',
           CASE PayType WHEN '建设银行卡' THEN SUM(Money) ELSE 0 END AS '建设银行卡'
    FROM Inpours
    GROUP BY CreateTime, PayType
) T
GROUP BY CreateTime

其实行转列,关键是要理清逻辑,而且对分组(Group by)概念比较清晰。上面两个列子基本上就是行转列的类型了。但是有个问题来了,上面是我为了说明弄的一个简单列子。实际中,可能支付方式特别多,而且逻辑也复杂很多,可能涉及汇率、手续费等等(曾经做个这样一个),如果支付方式特别多,我们的CASE WHEN 会弄出一大堆,确实比较恼火,而且新增一种支付方式,我们还得修改脚本如果把上面的脚本用动态SQL改写一下,我们就能轻松解决这个问题

代码

下面是通过PIVOT来进行行转列的用法,大家可以对比一下,确实要简单、更具可读性(呵呵,习惯的前提下)

代码
SELECT  
        CreateTime, [支付宝] , [手机短信], 
        [工商银行卡] , [建设银行卡] 
FROM 

    SELECT CONVERT(VARCHAR(10), CreateTime, 120) AS CreateTime,PayType, Money 
    FROM Inpours 
) P 
PIVOT ( 
            SUM(Money) 
            FOR PayType IN 
            ([支付宝], [手机短信], [工商银行卡], [建设银行卡]) 
      ) AS T 
ORDER BY CreateTime

有时可能会出现这样的错误:

消息 325,级别 15,状态 1,第 9 行

'PIVOT' 附近有语法错误。您可能需要将当前数据库的兼容级别设置为更高的值,以启用此功能。有关存储过程 sp_dbcmptlevel 的信息,请参见帮助。

这个是因为:对升级到 SQL Server 2005 或更高版本的数据库使用 PIVOT 和 UNPIVOT 时,必须将数据库的兼容级别设置为 90 或更高。有关如何设置数据库兼容级别的信息,请参阅 sp_dbcmptlevel (Transact-SQL)。 例如,只需在执行上面脚本前加上 EXEC sp_dbcmptlevel Test, 90; 就OK了, Test 是所在数据库的名称。

下面我们来看看列转行,主要是通过UNION ALL ,MAX来实现。假如有下面这么一个表

代码
CREATE TABLE ProgrectDetail 

    ProgrectName         NVARCHAR(20), --工程名称 
    OverseaSupply        INT,          --海外供应商供给数量 
    NativeSupply         INT,          --国内供应商供给数量 
    SouthSupply          INT,          --南方供应商供给数量 
    NorthSupply          INT           --北方供应商供给数量 
)  INSERT INTO ProgrectDetail 
SELECT 'A', 100, 200, 50, 50 
UNION ALL 
SELECT 'B', 200, 300, 150, 150 
UNION ALL 
SELECT 'C', 159, 400, 20, 320 
UNION ALL 
SELECT 'D', 250, 30, 15, 15

我们可以通过下面的脚本来实现,查询结果如下图所示

代码
SELECT ProgrectName, 'OverseaSupply' AS Supplier, 
        MAX(OverseaSupply) AS 'SupplyNum' 
FROM ProgrectDetail 
GROUP BY ProgrectName 
UNION ALL 
SELECT ProgrectName, 'NativeSupply' AS Supplier, 
        MAX(NativeSupply) AS 'SupplyNum' 
FROM ProgrectDetail 
GROUP BY ProgrectName 
UNION ALL 
SELECT ProgrectName, 'SouthSupply' AS Supplier, 
        MAX(SouthSupply) AS 'SupplyNum' 
FROM ProgrectDetail 
GROUP BY ProgrectName 
UNION ALL 
SELECT ProgrectName, 'NorthSupply' AS Supplier, 
        MAX(NorthSupply) AS 'SupplyNum' 
FROM ProgrectDetail 
GROUP BY ProgrectName

用UNPIVOT 实现如下:

代码
SELECT ProgrectName,Supplier,SupplyNum 
FROM  

    SELECT ProgrectName, OverseaSupply, NativeSupply, 
           SouthSupply, NorthSupply 
     FROM ProgrectDetail 
)T 
UNPIVOT  

    SupplyNum FOR Supplier IN 
    (OverseaSupply, NativeSupply, SouthSupply, NorthSupply ) 
) P
作者:潇湘隐者
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接.

(转载)重温SQL——行转列,列转行的更多相关文章

  1. 重温SQL——行转列,列转行

    行转列,列转行是我们在开发过程中经常碰到的问题.行转列一般通过CASE WHEN 语句来实现,也可以通过 SQL SERVER 2005 新增的运算符PIVOT来实现.用传统的方法,比较好理解.层次清 ...

  2. SQL行转列 (及EAV模型获取数据)

    参考文章: http://www.williamsang.com/archives/1508.html 情景简介 学校里面记录成绩,每个人的选课不一样,而且以后会添加课程,所以不需要把所有课程当作列. ...

  3. sql 行专列 列转行 普通行列转换

    转载:http://www.cnblogs.com/newwind521/archive/2010/11/25/1887203.html sql 行专列 列转行 普通行列转换 /* 标题:普通行列转换 ...

  4. SQL 行转列和列转行

    SQL 行转列和列转行 行列互转,是一个经常遇到的需求.实现的方法,有case when方式和2005之后的内置pivot和unpivot方法来实现. 在读了技术内幕那一节后,虽说这些解决方案早就用过 ...

  5. sql 行转列 PIVOT 列转行 UNPIVOT

    原文:sql 行转列 PIVOT 列转行 UNPIVOT 一: 现有表一(t_table1),想转为表二(t_table2)的格式. 表一: 年 公司 收入 2013 公司1 12 2013 公司2 ...

  6. SQL Server 行转列,列转行。多行转成一列

    一.多行转成一列(并以","隔开) 表名:A 表数据: 想要的查询结果: 查询语句: SELECT name , value = ( STUFF(( SELECT ',' + va ...

  7. SQL行转列,列转行

    SQL 行转列,列转行 行列转换在做报表分析时还是经常会遇到的,今天就说一下如何实现行列转换吧. 行列转换就是如下图所示两种展示形式的互相转换 行转列 假如我们有下表: SELECT * FROM s ...

  8. Sql server 中将数据行转列列转行(二)

    老规矩,先弄一波测试数据,数据填充代码没有什么意义,先折叠起来: /* 第一步:创建临时表结构 */ CREATE TABLE #Student --创建临时表 ( StuName ), --学生名称 ...

  9. SQL2005语句实现行转列,列转行

    在做报表时,经常需要将数据表中的行转列,或者列转行,如果不知道方法,你会觉得通过SQL语句来实现非常难.这里,我将使用pivot和unpivot来实现看似复杂的功能.这个功能在sql2005及以上版本 ...

随机推荐

  1. PICK定理模板

    PICK定理: S=I+O/2-1 S为多边形面积,I多边形内部的格点,O是多边形边上的格点 其中边上格点求法: 假设两个点A(x1,y1),B(x2,y2) 线段AB间格点个数为gcd(abs(x1 ...

  2. mysql备份脚本

    [root@AY130828161048465847Z ~]# vi mysqlbak.sh #!/bin/bash USERNAME=rootPASSWORD=mysqlDBNAME=test DA ...

  3. 【转】JVM 基础知识

    几年前写过一篇关于JVM调优的文章,前段时间拿出来看了看,又添加了一些东西.突然发现,基础真的很重要.学习的过程是一个由表及里,再由里及表的过程,所谓的“温故而知新”.而真正能走完这个轮回的人,也就能 ...

  4. ES5新特性:理解 Array 中增强的 9 个 API

    为了更方便的对JS中Array进行操作,ES5规范在Array的原型上新增了9个方法,分别是forEach.filter.map.reduce.reduceRight.some.every.index ...

  5. lightoj 1012

    水题,dfs #include<cstdio> #include<string> #include<cstring> #include<iostream> ...

  6. 处理Google Play的相关方法

    1.打开Google play软件的详细页面 Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.se ...

  7. OpenGL立方体在世界坐标系中_缩放_旋转_平移_顶点片源着色器_光照作用_棋盘纹理贴图

    读取bmp等图片格式中的像素还有难度,就先用这个棋盘图象素来弄了 代码打错一个就一直First-chance exception ,貌似还有一个要用q或者Q才能成功退出,不知道缺少哪句,我用窗口红叉退 ...

  8. ios之UI中自定义cell

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  9. 杨氏矩阵 leecode 提

    提交网址https://oj.leetcode.com/problems/search-a-2d-matrix/ 有个矩阵中的数,从左向右递增,从上而下递增,快速查找是一个数是是否存在,剑指offer ...

  10. poj 2892 Tunnel Warfare(线段树)

    Tunnel Warfare Time Limit: 1000MS   Memory Limit: 131072K Total Submissions: 7499   Accepted: 3096 D ...