重温SQL——行转列,列转行(转:http://www.cnblogs.com/kerrycode/archive/2010/07/28/1786547.html)...

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

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

ExpandedBlockStart.gif代码
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

 

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

ExpandedBlockStart.gif代码
SELECT 
      UserName, 
      
MAX(CASE Subject WHEN '语文' THEN Score ELSE 0 ENDAS '语文',
      
MAX(CASE Subject WHEN '数学' THEN Score ELSE 0 ENDAS '数学',
      
MAX(CASE Subject WHEN '英语' THEN Score ELSE 0 ENDAS '英语',
      
MAX(CASE Subject WHEN '生物' THEN Score ELSE 0 ENDAS '生物'
FROM dbo.[StudentScores]
GROUP BY UserName

查询结果如图所示,这样我们就能很清楚的了解每位学生所有的成绩了

 

 

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

 ExpandedBlockStart.gif代码

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''支付宝'501

INSERT INTO Inpours SELECT '张三''2010-06-14''支付宝'501

INSERT INTO Inpours SELECT '张三''2010-06-14''手机短信'1001

INSERT INTO Inpours SELECT '李四''2010-06-14''手机短信'1001

INSERT INTO Inpours SELECT '李四''2010-07-14''支付宝'1001

INSERT INTO Inpours SELECT '王五''2010-07-14''工商银行卡'1001

INSERT INTO Inpours SELECT '赵六''2010-07-14''建设银行卡'1001

 


下面来了一个统计数据的需求,要求按日期、支付方式来统计充值金额信息。这也是一个典型的行转列的例子。我们可以通过下面的脚本来达到目的
ExpandedBlockStart.gif代码
SELECT CONVERT(VARCHAR(10), CreateTime, 120AS CreateTime,
       
CASE PayType WHEN '支付宝'     THEN SUM(MoneyELSE 0 END AS '支付宝',
       
CASE PayType WHEN '手机短信'    THEN SUM(MoneyELSE 0 END AS '手机短信',
       
CASE PayType WHEN '工商银行卡'  THEN SUM(MoneyELSE 0 END AS '工商银行卡',
       
CASE PayType WHEN '建设银行卡'  THEN SUM(MoneyELSE 0 END AS '建设银行卡'
FROM Inpours
GROUP BY CreateTime, PayType

 

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

ExpandedBlockStart.gif代码

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

 

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

ExpandedBlockStart.gif代码
DECLARE @cmdText    VARCHAR(8000);
DECLARE @tmpSql        VARCHAR(8000);

SET @cmdText = 'SELECT CONVERT(VARCHAR(10), CreateTime, 120) AS CreateTime,' + CHAR(10);
SELECT @cmdText = @cmdText + ' CASE PayType WHEN ''' + PayType + ''' THEN SUM(Money) ELSE 0 END AS ''' + PayType 
                
+ ''',' + CHAR(10)  FROM (SELECT DISTINCT PayType FROM Inpours ) T

SET @cmdText = LEFT(@cmdTextLEN(@cmdText-2--注意这里,如果没有加CHAR(10) 则用LEFT(@cmdText, LEN(@cmdText) -1)

SET @cmdText = @cmdText + ' FROM Inpours     GROUP BY CreateTime, PayType ';

SET @tmpSql ='SELECT CreateTime,' + CHAR(10);
SELECT @tmpSql = @tmpSql + ' ISNULL(SUM(' + PayType  + '), 0) AS ''' + PayType  + ''','  + CHAR(10)
                    
FROM  (SELECT DISTINCT PayType FROM Inpours ) T

SET @tmpSql = LEFT(@tmpSqlLEN(@tmpSql-2+ ' FROM (' + CHAR(10);

SET @cmdText = @tmpSql + @cmdText + ') T GROUP BY CreateTime ';
PRINT @cmdText
EXECUTE (@cmdText);

 

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

ExpandedBlockStart.gif代码
SELECT 
        CreateTime, 
[支付宝] , [手机短信],
        
[工商银行卡] , [建设银行卡]
FROM
(
    
SELECT CONVERT(VARCHAR(10), CreateTime, 120AS 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 或更高。有关如何设置数据库兼容级别的信息,请参阅 。 例如,只需在执行上面脚本前加上 EXEC sp_dbcmptlevel Test, 90; 就OK了, Test 是所在数据库的名称。

 

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

ExpandedBlockStart.gif代码
CREATE TABLE ProgrectDetail
(
    ProgrectName         
NVARCHAR(20), --工程名称
    OverseaSupply        INT,          --海外供应商供给数量
    NativeSupply         INT,          --国内供应商供给数量
    SouthSupply          INT,          --南方供应商供给数量
    NorthSupply          INT           --北方供应商供给数量
)

INSERT INTO ProgrectDetail
SELECT 'A'1002005050
UNION ALL
SELECT 'B'200300150150
UNION ALL
SELECT 'C'15940020320
UNION ALL
SELECT 'D'250301515

 

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

ExpandedBlockStart.gif代码
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 实现如下:

ExpandedBlockStart.gif代码
SELECT ProgrectName,Supplier,SupplyNum
FROM 
(
    
SELECT ProgrectName, OverseaSupply, NativeSupply,
           SouthSupply, NorthSupply
     
FROM ProgrectDetail
)T
UNPIVOT 
(
    SupplyNum 
FOR Supplier IN
    (OverseaSupply, NativeSupply, SouthSupply, NorthSupply )
) P

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/298069.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php正则获取标签,PHP通过正则匹配中文字符 过滤html标签 获取文本内容

正则匹配中文汉字根据页面编码不同而略有区别:GBK/GB2312编码:[x80-xff] 或 [xa1-xff]UTF-8编码:[x{4e00}-x{9fa5}]/u[php]$str "账单123";//GBK/GB2312使用:preg_match_all("/[\x80-\xff]/", $str, $chines…

python监控机票价格_喜欢旅行又怕吃土?让Python来爬取最便宜机票吧!

图源:videoblocks.com你喜欢旅行吗?这个问题通常会得到一个肯定的答案,随后引出一两个有关之前冒险经历的故事。大多数人都认为旅行是体验新文化和开阔视野的好方法。但如果问题是“你喜欢搜索机票的过程吗?”也许话题就到此为止了…

伤不起的指针

虽然知道怎么做,但是还是做一遍啦。结果调试了两个多小时,真崩溃。 /* * * * Filename: intlist.h * * Description: * * Version: 1.0 * Created: 09/16/2011 02:56:13 AM * Revision: none * Compiler: gc…

[禅悟人生]尊严非席, 不可卷起

日本江户时期是一个社会很不稳定的时期,浪人武士依仗强力横行无忌。 有一个著名的茶师跟随着一个显赫的主人。 有一天主人要去京城办事,舍不得离开茶师,就说,你跟我去吧,好每天给我泡茶。茶师很害怕,对主人…

要男女朋友有什么用?

1 题:我不要面子的嘛!2 狗都有人给撑伞了。。3 这大概就是传说中的一见钟情吧4 5 南方人说话有多软糯6 男女朋友有什么用你点的每个赞,我都认真当成了喜欢

低代码应用创新成果——轴承行业数字化智造系统(含MES/ERP/WMS)

轴承是当代机械设备中一种不可或缺的零部件,广泛应用于汽车、铁路车辆及各类工业机械和家用电器等国民经济的重要领域,是一种节约能源、提高效率的伟大发明。轴承行业作为装备制造业的先锋,一直以来都走在数字化转型的前列,引领行…

城管威逼交警“让老百姓笑话”

城管威逼交警“让老百姓笑话” 发表时间:2010-07-05 09:15:42 来源:新华每日电讯我说两句(加入讨论) “给我们车贴罚单,你是不是不想干了?咱都是执法的,这一整,不让老百姓笑话吗?”这是沈阳市一位城管对正…

重点客户销售数据分析python_药品销售数据分析--python

一、数据分析的目的数据分析是指用适当的统计分析方法对收集来的大量数据进行分析,提取有用信息和形成结论而对数据加以详细研究和概括总结的过程。本篇文章中,假设以朝阳医院2018年销售数据为例,目的是了解朝阳医院在2018年里的销售情况&…

C#类与结构体究竟谁快——各种函数调用模式速度评测

以前我一直有个疑惑——在C#中,究竟是类(class)比较快,还是结构体(struct)比较快?当时没有深究。 最近我遇到一个难题,需要将一些运算大的指针操作代码给封装一下。原先为了性能&…

HelloSilverlight

一&#xff1a;输入姓名并选中一个日期&#xff0c;将在下面显示 二:XAML代码 <UserControl x:Class"HelloSilverlight.MainPage"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2…

Building a Space Station--POJ 2031

1、题目类型&#xff1a;计算几何&#xff0c;最小生成树。 2、解题思路&#xff1a;&#xff08;1&#xff09;获得所有点路径长度的矩阵map[][]&#xff1b;&#xff08;2&#xff09;利用Prim算法求解最小生成树。 3、注意事项&#xff1a;数学操作&#xff0c;中间值全部用…

21世纪7大数学难题,解决其中一个你就成为了百万富翁!

全世界只有3.14 % 的人关注了爆炸吧知识百万富翁你也可以昨天一大早&#xff0c;知识君就收到模友送的3枝红玫瑰。仔细一看&#xff0c;原来又是来跟知识君约稿的。。。知识君只能说&#xff1a;1900年&#xff0c;希尔伯特&#xff08;传送门&#xff09;在巴黎国际数学家代表…

在 Azure VM 上使用 Jitsi 搭建私人视频会议

点击上方蓝字 / 关注“汪宇杰博客”原文&#xff1a;Azure Tips And Tricks翻译&#xff1a;汪宇杰私人视频会议市面上有许多视频会议应用程序&#xff0c;例如 Zoom、Microsoft Teams 和 Skype。有时&#xff0c;您需要自己的服务&#xff0c;以让自己更安全并在自己的公司内部…

php 筛选数组,2020-07-24 php 通过数组键值对筛选数组

筛选数组 $listMenuArray([0] > Array([type] > 0[min] > 0)[1] > Array([type] > 1[min] > 1))目标数组 $resArray([0] > Array([id] > 183[type] > 0[min] > 0)[1] > Array([id] > 184[type] > 0[min] > 1)[2] > Array([id] &g…

python省市区三级联动_Django Admin实现三级联动的示例代码(省市区)

通过自定义Admin的模板文件实现省市区的三级联动.要求创建记录时,根据省>市>区的顺序选择依次显示对应数据.修改记录时默认显示已存在的数据.Modelclass Member(models.Model):name models.CharField(max_length100, verbose_name姓名)province models.CharField(max_l…

[LeetCode]119.Pascal#39;s Triangle II

题目 Given an index k, return the kth row of the Pascal’s triangle. For example, given k 3, Return [1,3,3,1]. Note: Could you optimize your algorithm to use only O(k) extra space? 思路 无 代码 /**------------------------------------* 日期&#xff1a…

2010.7.29 模式对话框

为什么点击ONOK后&#xff0c;对话框上的控件资源会被删除&#xff1f;OnOK做了什么事儿&#xff1f; 假如有一个对话框Class CMyDialog 我在CMyDialog中&#xff0c;声明了一个m_button&#xff0c;然后在OnInitDlg()中create这个buttton&#xff0c;即m_button.create() 然后…

终于有人做了我一直想做而不敢做的事。。

1 初中物理是不是学过&#xff0c;受力面积小&#xff0c;相应的压力就大&#xff5e;我觉得应该直接趴上去&#xff0c;一定行&#xff5e;反正我也是瞎说的2 不是我吹&#xff0c;换成是我&#xff0c;这包子能吃五屉3 交警蜀黍耐心的领着这位行人过马路&#xff0c;麻烦你快…

.NET 6 中的隐式命名空间引用

.NET 6 中的隐式命名空间引用Intro之前写过一篇隐式命名空间引用的大概介绍&#xff0c;在一些小的测试项目中也有在用&#xff0c;一直没作为示例给大家分享&#xff0c;主要原因在于之前看到了一个关于隐式命名空间引用的 Github issue 提到会有一些破坏性的变更&#xff0c;…

vscode函数跳转插件_人生苦短,我们为 Cocos Creator 开发的插件和工具

在使用 Cocos Creator 开发项目的过程中&#xff0c;为了提高开发效率我们开发了很多扩展插件&#xff0c;本文介绍常用的几款&#xff0c;抛砖引玉&#xff0c;希望给大家带来帮助。腾讯开心鼠英语网页扩展&#xff1a;运行时查看场景节点树Cocos Creator 本地项目通常会在 Ch…