开篇介绍

记得笔者在 2006年左右刚开始学习 SQL Server 2000 的时候,遇到一个面试题就是行转列,列转行的操作,当时写了很长时间的 SQL 语句最终还是以失败而告终。后来即使能写出来,也是磕磕碰碰的,虽然很能锻炼 SQL 功底,每次都要挣扎一番,溺水的感觉。记得SQL Server 2005 以后就有了 PIVOT 和 UNPIVOT 这两个函数,可以非常方便的实现行转列和列传行的操作,就不再那么挣扎了。后来,在一个 08 项目中,有一位新的女同事在改一个 ETL,发现 SSIS 包中有一个PIVOT 控件不知道怎么用就叫我帮忙。虽然我觉得花点时间还是可以搞定的,但是为了赶回家看一场球赛,找了一个不靠谱的接口就扔下她一个人给跑了。因为项目应该很急,每个人压力其实都很大,不记得是当天晚上就要交付还是第二天要交付。现在想想,很内疚也非常败人品,因为平时大家伙还都比较信任我,但是关键时刻跑了,确实有点不太负责任。今天正好整理到这一部分的笔记就想到了这个疙瘩,山东的那位妹子如果看到了,说声对不起吧!

SSIS 笔记整理到这几个地方,就来总结一下 PIVOT 的使用,如果之前不会用的,看了这篇文章就可以明白了。

测试代码

IF OBJECT_ID('T040_PRODUCT_SALES') IS NOT NULL
DROP TABLE T040_PRODUCT_SALES
GO
CREATE TABLE T040_PRODUCT_SALES
(
ID INT IDENTITY(1,1),
ProductName VARCHAR(20),
SaleMonth INT,
SalesCount INT
) -- Inserting test data
INSERT INTO T040_PRODUCT_SALES VALUES
('Bicycle',1,1),
('Shoes',2,2),
('Clothes',3,3),
('Books',4,4),
('Medicine',5,5),
('Drinks',6,6),
('Shoes',7,7),
('Books',1,2),
('Bicycle',1,3),
('Medicine',1,4),
('Clothes',1,5),
('Mobile Phone',1,6),
('Books',1,7),
('Medicine',1,8),
('Shoes',1,9),
('Bicycle',2,10)
SELECT ProductName,
SaleMonth,
SUM(SalesCount) AS SalesCount
FROM T040_PRODUCT_SALES
GROUP BY ProductName,
SaleMonth
ORDER BY ProductName,
SaleMonth

我们需要实现的效果是按产品名称,1月,2月,3月,4月,5月,6月 这七个列来显示 SalesCount 的总数。

怎么来实现这种行列转换效果,只要把下面这个点就理解清楚,照着写就可以实现。

/****
SELECT 非透视列,
[透视列 1] AS '列名1',
[透视列 2] AS '列名2',
[透视列 3] AS '列名3'
FROM (
-- 源数据
SELECT 非透视列,
透视列值的来源列,
需要聚合的值
FROM 表
)AS 别名
PIVOT
(
SUM(需要聚合的值)
FOR 透视列值的来源列 IN ([透视列 1],[透视列 2],[透视列 3])
)AS 别名
****/

对照上面的语法,我们弄清楚这些对应关系:

  • 非透视列 - 一般是第一列,把效果想出来,ProductName 就是位于第一列,它是非透视列。
  • 透视列 - 就是需要由列变为行的那些列,哪些行中的值需要作为列来显示? 1月 - 6月。
  • 透视列值的来源列 - 就是 SaleMonth,这列包含了 1月 - 6月的值。
  • 需要聚合的值 - 就是 SalesCount。

把这些需求理解了,就直接按照上面的这个语法就可以实现了,没有一点点多余的代码。

SELECT ProductName,
ISNULL([1],0) AS '1',
ISNULL([2],0) AS '2',
ISNULL([3],0) AS '3',
ISNULL([4],0) AS '4',
ISNULL([5],0) AS '5',
ISNULL([6],0) AS '6'
FROM(
SELECT ProductName,
SaleMonth,
SalesCount
FROM T040_PRODUCT_SALES
)AS Sales
PIVOT
(
SUM(SalesCount)
FOR SaleMonth IN([1],[2],[3],[4],[5],[6])
)AS PIVOTBL

SSIS 中 Pivot 的实现

在数据流中添加一个 OLE DB Source 并配置源测试表。

添加一个 Pivot 控件。

对 Pivot 的配置

Pivot Key - 透视列。透视列中的每一个值(去重之后)将会形成一个新的列。

Set Key - 非透视列,需要和透视列一起显示的聚合列。

Pivot Value - Pivot Key 和 Set Key 一起关联的结果值。

之所以要选择 Ignore un-matched pivot key values and report them after DataFlow execution 是因为:

Pivot 转换控件是一个静态的状态控件,它需要清楚的知道在 Pivot Key 中有哪些确定的值,它需要基于这些值来创建相应的输出列。勾选中它并执行一次包之后,就可以在 Progress/中看到唯一的 Pivot Key 列表,我们在设计的阶段就可以通过这些列表上的值来确定最终我们需要创建的列。

比如,确定了只需要 1月份 - 7月份的作为新的输出列,将这些只拷贝到指定的位置中,根据这些透视列来创建物理表中的透视列。

下面是创建之后的列的名称。

这样整个配置就完成了。

执行的时候查看一下数据,基本上反映出了行转列的结构变化。这不过这些数据本身上看上去有一些重复,且没有聚合。

因此我们应该在查询结果先把需要聚合好的内容聚合好,这样在 PIVOT 转换控件中就直接进行行,列转换而不是转换在聚合,这样效率更高一些。

按照 ProductName 和销售月份实现了对数据的行列转换,数据信息的易读性显而易见。

当然也可以看看 Show Advanced Editor 中的内容。

在这里可以看到 Pivot 转换控件之前有 3 个数据源列,在经过 Pivot 转换之后,其 Output Columns 变成 8 列向下输出。

更多 BI 文章请参看 BI 系列随笔列表 (SSIS, SSRS, SSAS, MDX, SQL Server)  如果觉得这篇文章看了对您有帮助,请帮助推荐,以方便他人在 BIWORK 博客推荐栏中快速看到这些文章。

微软BI 之SSIS 系列 - 在 SQL 和 SSIS 中实现行转列的 PIVOT 透视操作的更多相关文章

  1. 微软BI 之SSAS 系列 - 在SQL Server 2012 中开发 Analysis Services Multidimensional Project

    SQL Server 2012 中提供了开发 SSAS 项目的两种模型,一种是新增加的 Tabular Model 表格模型,另一种就是原始的 Multidimensional Model 多维模型. ...

  2. 微软BI 之SSAS 系列 - 在 SQL Server 2012 下查看 SSAS 分析服务的模型以及几个模型的简单介绍

    在SSDT中部署一个 SSAS 项目到本地服务器上出现错误. You cannot deploy the model because the localhost deployment server i ...

  3. 微软BI 之SSRS 系列 - 如何在 MDX 查询中获取有效的 MEMBER 成员属性作为参数传递

    这篇小文章的来源是 天善问答,比如在报表中要根据点击某一个成员名称然后作为参数传递给自身报表或者下一张报表,这个在普通的 SQL 查询中没有任何问题.但是在 MDX 中查询是有区别的,比如在 MDX ...

  4. 【HANA系列】SAP HANA SQL取表中每行最小值

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA SQL取表中每 ...

  5. sql中的行转列和列转行的问题

    sql中的行转列和列转行的问题 这是一个常见的问题,也是一个考的问题 1.行转列的问题  简单实例 CREATE TABLE #T ( MON1 INT, MON2 INT, MON3 INT ) G ...

  6. SQL将一个表中的某一列值全部插入到另一个表中

    1.  SQL将一个表中的某一列值全部插入到另一个表中 插入的话: insert into a(col) select col from b; 更新的话: update a set col=selec ...

  7. 在论坛中出现的比较难的sql问题:42(动态行转列 考勤时间动态列)

    原文:在论坛中出现的比较难的sql问题:42(动态行转列 考勤时间动态列) 所以,觉得有必要记录下来,这样以后再次碰到这类问题,也能从中获取解答的思路.

  8. 在论坛中出现的比较难的sql问题:39(动态行转列 动态日期列问题)

    原文:在论坛中出现的比较难的sql问题:39(动态行转列 动态日期列问题) 最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了. 所以,觉 ...

  9. 在论坛中出现的比较难的sql问题:37(动态行转列 某一行数据转为列名)

    原文:在论坛中出现的比较难的sql问题:37(动态行转列 某一行数据转为列名) 所以,觉得有必要记录下来,这样以后再次碰到这类问题,也能从中获取解答的思路.

随机推荐

  1. no 'object' file generated

    c++编译的时候出现如下的错误: no 'object' file generated   [解决方法] Go to project properties -> configurations p ...

  2. 线程池的使用ExecutorService

    private ExecutorService executorService = Executors.newFixedThreadPool(5); // 引入线程池来管理多线程 private vo ...

  3. Linux 文件锁

    当多个进程同时访问操作同一个文件时,我们怎么保证文件数据的正确性. linux通常采用的方法是文件上锁,来避免共享资源的产生竞争状态. 文件锁包括建议性锁和强制性的锁: 建议性的锁 :顾名思义,相对温 ...

  4. BackgroundWorker控件

    在我们的程序中,经常会有一些耗时较长的运算,为了保证用户体验,不引起界面不响应,我们一般会采用多线程操作,让耗时操作在后台完成,完成后再进行处理或给出提示,在运行中,也会时时去刷新界面上的进度条等显示 ...

  5. SQL SERVER 修改数据库名称(包括 db.mdf 名称的修改)

    刚开始学习SQL SERVER 2005,弄了一个上午修改数据库名,主要是需要修改db.mdf 和db_log.ldf的名字,总算解决了.在这里记下,以后再要修改了就别忘了. 假设原来数据库名为db, ...

  6. nyoj 737 石子合并(一)。区间dp

    http://acm.nyist.net/JudgeOnline/problem.php?pid=737 数据很小,适合区间dp的入门 对于第[i, j]堆,无论你怎么合并,无论你先选哪两堆结合,当你 ...

  7. ORACLE服务端详细安装步骤(配图解)

    ORACLE服务端的安装及配置 l 将下载的安装包解压缩,双击[setup.exe]文件,系统检查监听参数,耐心等待,完成后出现如下界面,电子邮件可不填,"我希望..."建议不勾选 ...

  8. java多线程学习-同步之线程通信

    这个示例是网上烂大街的,子线程循环100次,主线程循环50次,但是我试了很多次,而且从网上找了很多示例,其实多运行几次,看输出结果并不正确.不知道是我转牛角尖了,还是怎么了.也没有大神问,好痛苦.现在 ...

  9. js alert重写,适用于手机端,改自于网上的代码

    <!DOCTYPE html> <html> <head> <meta name="viewport" content="wid ...

  10. mysql的一个特殊问题 you can't specify target table 'cpn_regist' for update in FROM clause

    今天在操作数据库的时候遇到了一个问题,sql语句如下: UPDATE cpn_yogurt_registration SET dep1Name = '1' WHERE `key` in  (SELEC ...