重温SQL——行转列,列转行
我们首先先通过一个老生常谈的例子,学生成绩表(下面简化了些)来形象了解下行转列
CREATE TABLE [StudentScores]
(
[UserName] NVARCHAR(), --学生姓名
[Subject] NVARCHAR(), --科目
[Score] FLOAT, --成绩
) INSERT INTO [StudentScores] SELECT 'Nick', '语文', INSERT INTO [StudentScores] SELECT 'Nick', '数学', INSERT INTO [StudentScores] SELECT 'Nick', '英语', INSERT INTO [StudentScores] SELECT 'Nick', '生物', INSERT INTO [StudentScores] SELECT 'Kent', '语文', INSERT INTO [StudentScores] SELECT 'Kent', '数学', INSERT INTO [StudentScores] SELECT 'Kent', '英语', INSERT INTO [StudentScores] SELECT 'Kent', '生物',
如果我想知道每位学生的每科成绩,而且每个学生的全部成绩排成一行,这样方便我查看、统计,导出数据
SELECT
UserName,
MAX(CASE Subject WHEN '语文' THEN Score ELSE END) AS '语文',
MAX(CASE Subject WHEN '数学' THEN Score ELSE END) AS '数学',
MAX(CASE Subject WHEN '英语' THEN Score ELSE END) AS '英语',
MAX(CASE Subject WHEN '生物' THEN Score ELSE END) AS '生物'
FROM dbo.[StudentScores]
GROUP BY UserName
查询结果如图所示,这样我们就能很清楚的了解每位学生所有的成绩了

接下来我们来看看第二个小列子。有一个游戏玩家充值表(仅仅为了说明,举的一个小例子),
CREATE TABLE [Inpours]
(
[ID] INT IDENTITY(,),
[UserName] NVARCHAR(), --游戏玩家
[CreateTime] DATETIME, --充值时间
[PayType] NVARCHAR(), --充值类型
[Money] DECIMAL, --充值金额
[IsSuccess] BIT, --是否成功 1表示成功, 0表示失败
CONSTRAINT [PK_Inpours_ID] PRIMARY KEY(ID)
) INSERT INTO Inpours SELECT '张三', '2010-05-01', '支付宝', , INSERT INTO Inpours SELECT '张三', '2010-06-14', '支付宝', , INSERT INTO Inpours SELECT '张三', '2010-06-14', '手机短信', , INSERT INTO Inpours SELECT '李四', '2010-06-14', '手机短信', , INSERT INTO Inpours SELECT '李四', '2010-07-14', '支付宝', , INSERT INTO Inpours SELECT '王五', '2010-07-14', '工商银行卡', , INSERT INTO Inpours SELECT '赵六', '2010-07-14', '建设银行卡', ,
下面来了一个统计数据的需求,要求按日期、支付方式来统计充值金额信息。这也是一个典型的行转列的例子。我们可以通过下面的脚本来达到目的
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([支付宝]) , ) AS [支付宝] ,
ISNULL(SUM([手机短信]) , ) AS [手机短信] ,
ISNULL(SUM([工商银行卡]), ) AS [工商银行卡] ,
ISNULL(SUM([建设银行卡]), ) AS [建设银行卡]
FROM
(
SELECT CONVERT(VARCHAR(), CreateTime, ) AS CreateTime,
CASE PayType WHEN '支付宝' THEN SUM(Money) ELSE END AS '支付宝' ,
CASE PayType WHEN '手机短信' THEN SUM(Money) ELSE END AS '手机短信',
CASE PayType WHEN '工商银行卡' THEN SUM(Money) ELSE END AS '工商银行卡',
CASE PayType WHEN '建设银行卡' THEN SUM(Money) ELSE END AS '建设银行卡'
FROM Inpours
GROUP BY CreateTime, PayType
) T
GROUP BY CreateTime
其实行转列,关键是要理清逻辑,而且对分组(Group by)概念比较清晰。上面两个列子基本上就是行转列的类型了。但是有个问题来了,上面是我为了说明弄的一个简单列子。实际中,可能支付方式特别多,而且逻辑也复杂很多,可能涉及汇率、手续费等等(曾经做个这样一个),如果支付方式特别多,我们的CASE WHEN 会弄出一大堆,确实比较恼火,而且新增一种支付方式,我们还得修改脚本如果把上面的脚本用动态SQL改写一下,我们就能轻松解决这个问题
代码 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->DECLARE @cmdText VARCHAR(8000);
DECLARE @tmpSql VARCHAR(); SET @cmdText = 'SELECT CONVERT(VARCHAR(10), CreateTime, 120) AS CreateTime,' + CHAR(); SELECT @cmdText = @cmdText + ' CASE PayType WHEN ''' + PayType + ''' THEN SUM(Money) ELSE 0 END AS ''' + PayType
+ ''',' + CHAR() FROM (SELECT DISTINCT PayType FROM Inpours ) T SET @cmdText = LEFT(@cmdText, LEN(@cmdText) -) --注意这里,如果没有加CHAR() 则用LEFT(@cmdText, LEN(@cmdText) -) SET @cmdText = @cmdText + ' FROM Inpours GROUP BY CreateTime, PayType '; SET @tmpSql ='SELECT CreateTime,' + CHAR(); SELECT @tmpSql = @tmpSql + ' ISNULL(SUM(' + PayType + '), 0) AS ''' + PayType + ''',' + CHAR() FROM (SELECT DISTINCT PayType FROM Inpours ) T SET @tmpSql = LEFT(@tmpSql, LEN(@tmpSql) -) + ' FROM (' + CHAR(); SET @cmdText = @tmpSql + @cmdText + ') T GROUP BY CreateTime '; PRINT @cmdText EXECUTE (@cmdText);
下面是通过PIVOT来进行行转列的用法,大家可以对比一下,确实要简单、更具可读性(呵呵,习惯的前提下)
[工商银行卡] , [建设银行卡]
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来实现。假如有下面这么一个表
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
我们可以通过下面的脚本来实现,查询结果如下图所示
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 实现如下:
FROM
(
SELECT ProgrectName, OverseaSupply, NativeSupply,
SouthSupply, NorthSupply
FROM ProgrectDetail
)T
UNPIVOT
(
SupplyNum FOR Supplier IN
(OverseaSupply, NativeSupply, SouthSupply, NorthSupply )
) P
重温SQL——行转列,列转行的更多相关文章
- (转载)重温SQL——行转列,列转行
原文地址:http://www.cnblogs.com/kerrycode/archive/2010/07/28/1786547.html 行转列,列转行是我们在开发过程中经常碰到的问题.行转列一般通 ...
- SQL行转列 (及EAV模型获取数据)
参考文章: http://www.williamsang.com/archives/1508.html 情景简介 学校里面记录成绩,每个人的选课不一样,而且以后会添加课程,所以不需要把所有课程当作列. ...
- SQL 行转列和列转行
SQL 行转列和列转行 行列互转,是一个经常遇到的需求.实现的方法,有case when方式和2005之后的内置pivot和unpivot方法来实现. 在读了技术内幕那一节后,虽说这些解决方案早就用过 ...
- sql 行专列 列转行 普通行列转换
转载:http://www.cnblogs.com/newwind521/archive/2010/11/25/1887203.html sql 行专列 列转行 普通行列转换 /* 标题:普通行列转换 ...
- sql 行转列 PIVOT 列转行 UNPIVOT
原文:sql 行转列 PIVOT 列转行 UNPIVOT 一: 现有表一(t_table1),想转为表二(t_table2)的格式. 表一: 年 公司 收入 2013 公司1 12 2013 公司2 ...
- SQL Server 行转列,列转行。多行转成一列
一.多行转成一列(并以","隔开) 表名:A 表数据: 想要的查询结果: 查询语句: SELECT name , value = ( STUFF(( SELECT ',' + va ...
- SQL行转列,列转行
SQL 行转列,列转行 行列转换在做报表分析时还是经常会遇到的,今天就说一下如何实现行列转换吧. 行列转换就是如下图所示两种展示形式的互相转换 行转列 假如我们有下表: SELECT * FROM s ...
- Sql server 中将数据行转列列转行(二)
老规矩,先弄一波测试数据,数据填充代码没有什么意义,先折叠起来: /* 第一步:创建临时表结构 */ CREATE TABLE #Student --创建临时表 ( StuName ), --学生名称 ...
- SQL2005语句实现行转列,列转行
在做报表时,经常需要将数据表中的行转列,或者列转行,如果不知道方法,你会觉得通过SQL语句来实现非常难.这里,我将使用pivot和unpivot来实现看似复杂的功能.这个功能在sql2005及以上版本 ...
随机推荐
- [TJOI2014]Alice and Bob[拓扑排序+贪心]
题意 给出一个序列的以每一项结尾的 \(LIS\) 的长度a[],求一个序列,使得以每一项为开头的最长下降子序列的长度之和最大. \(n\leq 10^5\) . 分析 最优解一定是一个排列,因为如果 ...
- mysql查询操作之单表查询、多表查询、子查询
一.单表查询 单表查询的完整语法: .完整语法(语法级别关键字的排列顺序如下) select distinct 字段1,字段2,字段3,... from 库名.表名 where 约束条件 group ...
- htm5移动端开发 和 pc端开发
htm5移动端开发: 移动端开发需要注意的一些事情:http://www.duanliang920.com/learn/web/html5/321.html HTML5移动端手机网站开发流程:http ...
- 初学者浅谈我对领域驱动设计(DDD)的理解
一.为什么要学习领域驱动设计 如果你已经设计出了优雅而万能的软件架构,如果你只是想做一名高效的编码程序员,如果你负责的软件并不复杂,那你确实不需要学习领域驱动设计. 如果用领域驱动设计带来的收获: 能 ...
- fiddler抓包工具教程
Fiddler是一个蛮好用的抓包工具,可以将网络传输发送与接受的数据包进行截获.重发.编辑.转存等操作.也可以用来检测网络安全.反正好处多多,举之不尽呀!当年学习的时候也蛮费劲,一些蛮实用隐藏的小功能 ...
- log4cpp简单使用及踩到的坑
log4cpp是log4j的一个扩展, C++开发者可用该库记录日志,可输出到终端,亦可保存到文件. 下面简单demo展示如何输出日志到输出终端. #include <iostream> ...
- node.js主从分布式爬虫
前言 前文介绍过用Python写爬虫,但是当任务多的时候就比较慢, 这是由于Python自带的http库urllib2发起的http请求是阻塞式的,这意味着如果采用单线程模型,那么整个进程的大部分时间 ...
- <React Native移动开发实战>-1-React Native的JSX解决方案
JSX并不是一门新的开发语言,而是Facebook提出的语法方案:一种可以在JavaScript代码中直接书写HTML标签的语法糖,所以,JSX本质上还是JavaScript语言. 小知识:语法糖(S ...
- RBC:Echo设备2020年可为亚马逊贡献100亿美元收入
BI 中文站 12 月 22 日报道 加拿大皇家银行资本市场(RBC Capital Markets)分析师马克-马哈尼(Mark Mahaney)表示,亚马逊是首批将智能音箱引进主流受众的公司之一, ...
- printf命令详解
基础命令学习目录首页 本文是Linux Shell系列教程的第(八)篇,更多shell教程请看:Linux Shell系列教程 在上一篇:Linux Shell系列教程之(七)Shell输出这篇文章中 ...