关于UNPIVOT 操作符

UNPIVOT 操作符说明

简而言之,UNPIVOT操作符就是取得一个行的数据集合,然后把每一行都转换成多个行数据。为了更好地理解,请看下图:

图1

从上图中,你能发现UNPOVOT操作符,取得了两行数据,每行包含三个Price值,然后将这些转化成6行数据,其中每个产品价格都是一个不同的行。

UNPIVOT 命令制定了两个不同的列类型。第一个类型是列中不被转换的。在例子中,ID、产品名字列是这样的列类型。第二种列类型就是那些被转换的。诸如ProductCode, Wholesale 和Retail 这三列。在我上面的例子中,那些没有被转换的列将被在每套列值集合中重复,而另外的那些列将被转换成行。

UNPIVOT 语法

下面就是 UNPIVOT 的语法:

SELECT [columns not unpivoted],
	 [unpivot_column],
       [value_column],
FROM
(<source query>)
AS <alias for the source data>
UNPIVOT ( [value_column] FOR [unpivot_column] IN ( <column_list> ) ) 
   AS <alias for unpivot>

Where:

  • [columns not unpivoted]: 不被转换的列的名字清单。
  • [unpivot_column]: 不转换的列的名称。
  • [value_column]: 确定一个列名称来代表不转换的列的数据。
  • <source query>: 源数据。

  •       <alias for the source data>: 为源数据转换后的表确定一个别名。
  •       <column_list>:  被转换的列的列名称。
  •      <alias for unpivot>: 为转换操作的整套生产,确定一个别名。

为了更好地理解我们看下面的例子:

简单的例子

USE tempdb;
GO
IF object_id('PhoneNumbers') IS NOT NULL DROP TABLE PhoneNumbers;
GO
CREATE TABLE PhoneNumbers (
	PersonID int, 
	HomePhone varchar(12),
	CellPhone varchar(12), 
	Workphone varchar(12), 
	FaxNumber varchar(12));

INSERT INTO PhoneNumbers VALUES 
	(1,Null,'444-555-2931',Null,Null),
	(2,'444-555-1950','444-555-2931',Null, Null),
	(3,'444-555-1950', Null,'444-555-1324','444-555-2310'),
	(4,'444-555-1950','444-555-2931','444-555-1324',
        '444-555-1987');

Listing 1: 创建并填充PhoneNumbers 数据

SELECT PersonID, PhoneType, PhoneNumber
FROM (
	SELECT PersonID, HomePhone, CellPhone, Workphone, FaxNumber
	FROM PhoneNumbers ) AS Src
	UNPIVOT (
		PhoneNumber FOR PhoneType IN 
		(HomePhone, CellPhone, WorkPhone, FaxNumber)) AS UNPVT;

Listing 2: 行列转换语法例子

执行上面代码后显示如下图:

通过这个例子,我们发现执行结果中每行数据只包含一个单一的电话号码,同时注意到结果中在原表中有几个号码不为null则有几行数据,ID也就有几次。接下来我们进一步通过使用UNPIVOT来加深认识。

使用两个UNPIVOT操作符

第二个例子中,我将使用两个操作符来行转列来转换一套名字/值 的两列数据。具体如下:

在表 CustPref里面 我有四对名称和值。

我们将使用不同的UNPIVOT操作符来创建一个结果集,每一个PrefType的名字和值针对每个CustID 和CustName。并联使用操作符的作用是为了转换两组列。这样讲能表示为一个参数名称和值在一行里面。执行代码如下:

http://www.cnblogs.com/wenBlog/

通过这个输出结果,能发现不同的type对应不同的值得列,并且要关联CustID。整个查询通过两个不同的UNPOVOT操作符同时使用了where 子句来合并输出结果(基于列名前五个字符相同的进行匹配),第一个行转列转换的是数据,第二个为类型,where限制了比较前五个字符,我能取得匹配的数据组。

动态UNPIVOT查询

代码如下:

USE tempdb;
GO
DECLARE @ColNames varchar(1000);
SET @ColNames = '';
-- Get PrefValue Columns
SELECT @ColNames=stuff((
    SELECT DISTINCT ',' + QUOTENAME(COLUMN_NAME)
    FROM INFORMATION_SCHEMA.COLUMNS p2
    WHERE TABLE_NAME = 'CustPref'
	  AND COLUMN_NAME like 'Pref_Type'
    FOR XML PATH(''), TYPE).value('.', 'varchar(max)')
            ,1,1,'')
-- Get PrefType Columns
DECLARE @ColValues varchar(1000);
SET @ColValues = '';
SELECT @ColValues=stuff((
    SELECT DISTINCT ',' + QUOTENAME(COLUMN_NAME)
    FROM INFORMATION_SCHEMA.COLUMNS p2
    WHERE TABLE_NAME = 'CustPref'
	  AND COLUMN_NAME like 'Pref_Data'
    FOR XML PATH(''), TYPE).value('.', 'varchar(max)')
            ,1,1,'')
-- Generate UNPIVOT Statement
DECLARE @CMD nvarchar(2000);
SET @CMD = 'SELECT CustId, CustName, PrefType, PrefValue FROM ' + 
           '(SELECT CustID, CustName, ' + @ColNames + ',' + @ColValues + 
		   ' FROM CustPref) AS Perf UNPIVOT (PrefValue FOR PrefValues IN (' +  
		   @ColValues + ')) AS UP1 UNPIVOT (PrefType FOR PrefTypes IN (' + 
		   @ColNames + ')) AS UP2 WHERE ' + 
		   'substring(PrefValues,5,1) = substring(PrefTypes,5,1);'
-- Print UNPIVOT Command
PRINT @CMD
-- Execute UNPIVOT Command
execute sp_executesql @CMD

结果是与上面的例子完全相同的。

为了完成和这个动态的SQL,我使用了INFORMATION_SCHEMA.COLUMNS视图。这个视图能帮我们设定两个变量@ColNames和@ColValues ,这就包含了用逗号区分的列名的字符串。这两个变量被用来构建动态的行转列查询。一旦我建立了动态的SQL就能,执行这个sp_executesql了。

这是一个简单的实例,但是相同的逻辑可以应用于更多的不同的组列的转换。

Summary

UNPIVOT操作符在2005 首次被引入,允许我们将多个name/value 列从不规范的表中创建到一个规范画的结果集中,并且一一对应于选定的列。通过使用这个操作符,我们能同时转换多个不同组的name/value 的成对的列。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据和云

MySQL的前缀索引及Oracle的类似实现

MySQL有一个很有意思的索引类型,叫做前缀索引,它可以给某个文本字段的前面部分单独做索引,从而降低索引的大小。 其实,Oracle也有类似的实现,对于文本,它...

3745
来自专栏北京马哥教育

Mysql 架构和索引

字段类型选择 慷慨是不明智的 在相关的表中使用相同的数据类型,因为可能进行join 选择标示符:整数通常是最佳选择,尽量避免使用字符串 大致决定数据类型(数字,...

3459
来自专栏CaiRui

SQLAlchemy外键的使用

orm可以将数据库存储的数据封装成对象,同时,如果封装的好的话,所有的数据库操作都可以封装到对象中。这样的代码在组织结构上会非常的清晰,并且相对与使用sql语句...

2635
来自专栏栗霖积跬步之旅

单例模式

定义:   单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。即一个类只有一个对象...

1979
来自专栏数据和云

SQL 使用like &#39;%ABC&#39; 和 like &#39;%ABC%&#39;的优

一般情况下,sql中使用col_name like 'ABC%‘的情况才能使用到col_name字段上的索引。那么如果是col_name like '%ABC%...

5038
来自专栏技术专栏

SQL 编写技巧总结(持续更新中)

参考 http://blog.csdn.net/bitcarmanlee/article/details/51926530

1673
来自专栏小筱月

SSM框架的sql中参数注入(#和$的区别)

ORDER BY ${order} 和模糊查询 username LIKE '%${username}%' 是用$符号,其他的大多是用 #{} 来获取传递的参数...

1752
来自专栏转载gongluck的CSDN博客

逆转字符串

逆转字符串——输入一个字符串,将其逆转并输出 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #inc...

2874
来自专栏Python爬虫实战

MySQL 从零开始:09 计算字段

在数据库中存储公司信息,一般用两个表列分别表示公司名和公司地址。 如果想要在一个字段中既显示公司名,又要显示公司地址,那么就需要对已有字段进行处理了,这个处理过...

722
来自专栏xingoo, 一个梦想做发明家的程序员

Java几种单例模式的实现与利弊

多线程环境下无法保证单例效果,会多次执行 instance=new Singleton(),需要考虑到多线程

2402

扫码关注云+社区