设为首页 收藏本站
查看: 1275|回复: 0

[经验分享] [学习SQL SERVER 2005系列]感受新功能二:UNPIVOT

[复制链接]

尚未签到

发表于 2016-11-5 08:51:06 | 显示全部楼层 |阅读模式

  • [学习SQLSERVER2005系列]感受新功能二:UNPIVOT

  • 今晚我们就看看SQL2005这个UNPIVOT吧。UNPIVOT几乎完全是PIVOT相反的操作,将列转换为行。它和PIVOT关系运算符一样对表值表达式进行操作以获得另一个表。记得我们在SQL2000中要用UNIONALL来把多列合并到一列的情况吧,同样对于列不定时,我们往往还利用系统表syscolumns来构造动态SQL,然后用EXEC来运行。
  • 环境准备:
  • ------------------------------------
  • --Author:happyflsytone
  • --Date:2008-09-2214:05:26
  • ------------------------------------

  • --TestData:ta
  • IFOBJECT_ID('ta')ISNOTNULL
  • DROPTABLEta
  • Go
  • CREATETABLEta(col1nvarchar(2),col2nvarchar(2),Anvarchar(1),Bnvarchar(1),Cnvarchar(1))
  • Go
  • INSERTINTOta
  • select'HN','CD','0','3','0'unionall
  • select'HN','CS','1','2','0'unionall
  • select'HN','HY','0','0','4'
  • GO

  • 我们先来回顾SQL2000的行列转换,比如我们对上例程把abcg列转行显示。我们分两种情况来讨论:
  • 一、当col3的列值固定就是A、B、C三列的情况

  • SELECTCOL1,COL2,NEWCOL='A',A
  • FROMTA
  • UNIONALL
  • SELECTCOL1,COL2,NEWCOL='B',B
  • FROMTA
  • UNIONALL
  • SELECTCOL1,COL2,NEWCOL='C',C
  • FROMTA
  • ORDERBYCOL1,COL2,NEWCOL

  • /*
  • COL1COL2NEWCOLA
  • -------------------------
  • HNCDA0
  • HNCDB3
  • HNCDC0
  • HNCSA1
  • HNCSB2
  • HNCSC0
  • HNHYA0
  • HNHYB0
  • HNHYC4
  • */
  • 二、当除COL1COL2外的列很多时如果我们还一个一个写unionall就会很累了,这时我们往往读系统表构造SQL串,其实也就是构造一个select....unionallselect....SQL字符串
  • DECLARE@SVARCHAR(8000)
  • SELECT@S=ISNULL(@S+'
  • UNIONALL','')+'
  • SELECTCOL1,COL2,NEWCOL='''+NAME+''','+NAME+'ASNEWCOLVFROMTA'
  • FROMSYS.COLUMNSWHEREOBJECT_ID=OBJECT_ID('TA')ANDNAMENOTIN('COL1','COL2')
  • PRINT(@S+'
  • ORDERBYCOL1,COL2,NEWCOL')
  • /*
  • COL1COL2NEWCOLNEWCOLV
  • -------------------------
  • HNCDA0
  • HNCDB3
  • HNCDC0
  • HNCSA1
  • HNCSB2
  • HNCSC0
  • HNHYA0
  • HNHYB0
  • HNHYC4
  • */
  • 我们先输入这个@S看看是什么东东,只要加上print@s

  • SELECTCOL1,COL2,NEWCOL='A',AASNEWCOLVFROMTA
  • UNIONALL
  • SELECTCOL1,COL2,NEWCOL='B',BASNEWCOLVFROMTA
  • UNIONALL
  • SELECTCOL1,COL2,NEWCOL='C',CASNEWCOLVFROMTA
  • ORDERBYCOL1,COL2,NEWCOL

  • 其实就是上面我们构造的固定列值的SQL嘛。
  • 好,现在们开始在2005中实现这个功能,先来看看2005的FROM子句的定义(关于如何看这个定义请参照SQL2005的文档约定及Transate-SQL语法约定):
  • [FROM{<table_source>}[,...n]]
  • <table_source>::=
  • {
  • <unpivoted_table>
  • }
  • <unpivoted_table>::=
  • table_sourceUNPIVOT<unpivot_clause>table_alias

  • <unpivot_clause>::=
  • (value_columnFORpivot_columnIN(<column_list>))
  • <column_list>::=
  • column_name[,...]

  • pivot_column和value_column是UNPIVOT运算符使用的组合列。指定输入表从column_list中的多个列缩减为名为pivot_column的单个列。
  • 注意了,我们这儿默认你ABC列的类型是一致的。

  • 接着我们利用我们开头的例子来理解一下这个FROM子句,很显然我们的[NEWCOLV]对应上面的value_column,我们还假定列会下固定为这三项,那么列[NEWCOL]对应上面的pivot_column,进而我们应该得出[a],,[c]是column_name即我们要合并的列,最后我们只要构造一下table_source就可以了,如何构造这个table_source,显然pivot_column和value_column新生成的列,其它就应该是你想要分组的列啦.
  • 我们来总结一下:这个FROM子句是基于table_source对pivot_column进行透视,table_source中pivot_column和value_column列之外的列被称为透视运算符的组合列,而UNPIVOT是对输入表执行列的合并操作,并为每个单列返回一新行(二列包含当前列的列名及列值),好,我们试着写出这个SQL:

  • SELECTcol1,col2,[NEWCOL],[NEWCOLV]
  • FROM
  • (SELECTcol1,col2,A,B,C
  • FROMta)p
  • UNPIVOT
  • (NEWCOLV
  • FORNEWCOLIN(A,B,C)
  • )ASunpvt

  • 我们执行一下看看结果:(为了使输出好看,我对newcol做了处理,只要把[NEWCOL]改写成CAST([NEWCOL]ASVARCHAR(2))AS[NEWCOL]即可,至于什么差别大家一试就知道。)

  • /*
  • col1col2NEWCOLNEWCOLV
  • ---------------------
  • HNCDA0
  • HNCDB3
  • HNCDC0
  • HNCSA1
  • HNCSB2
  • HNCSC0
  • HNHYA0
  • HNHYB0
  • HNHYC4

  • (9行受影响)
  • */


  • 当然在2005中列值不固定时也要用到动态SQL,我们把这个例子完成如下:
  • DECLARE@sVARCHAR(1000)
  • SELECT@s=isnull(@s+',','')+'['+ltrim(NAME)+']'
  • FROMSYS.COLUMNSWHEREOBJECT_ID=OBJECT_ID('TA')ANDNAMENOTIN('COL1','COL2')
  • ;
  • EXEC('SELECTcol1,col2,NEWCOL,NEWCOLV
  • FROM
  • (SELECTcol1,col2,'+@s+'
  • FROMTA)p
  • UNPIVOT
  • (NEWCOLV
  • FORNEWCOLIN('+@s+')
  • )ASunpvt')



  • /*
  • col1col2NEWCOLNEWCOLV
  • -----------------------------------------------------------------------------------------------------------------------------------------------
  • HNCDA0
  • HNCDB3
  • HNCDC0
  • HNCSA1
  • HNCSB2
  • HNCSC4
  • HNHYA0
  • HNHYB0
  • HNHYC4

  • (9行受影响)

  • */
  • 下面我们对特殊情况做点补充,顺便一起来复习一下PIVOT操作。当我们上面的例程中的ABC三列出NULL时,结果会什么样呢,首先们利用PIVOT来生成我所说的这种带有NULL示例数据:
  • ------------------------------------
  • --Author:happyflsytone
  • --Date:2008-09-2214:33:20
  • ------------------------------------

  • --TestData:ta
  • IFOBJECT_ID('ta')ISNOTNULL
  • DROPTABLEta
  • Go
  • CREATETABLEta(col1nvarchar(2),col2nvarchar(2),NEWCOLnvarchar(1),NEWCOLVint)
  • Go
  • INSERTINTOta
  • select'HN','CD','A','0'unionall
  • select'HN','CD','B',nullunionall
  • select'HN','CD','C','0'unionall
  • select'HN','CS','A','1'unionall
  • select'HN','CS','B','2'unionall
  • select'HN','CS','C',nullunionall
  • select'HN','HY','A','0'unionall
  • select'HN','HY','B','0'unionall
  • select'HN','HY','C','4'
  • GO
  • --Start
  • SELECTcol1,col2,[A],[B],[C]
  • FROM
  • (SELECTCOL1,COL2,newcolv,newcol
  • FROMTA)P
  • PIVOT
  • (SUM(NEWCOLV)
  • FORNEWCOLIN([A],[B],[C])
  • )ASunpvt
  • --Result:
  • /*
  • col1col2ABC
  • -----------------------------------------
  • HNCD0NULL0
  • HNCS12NULL
  • HNHY004

  • (3行受影响)

  • */
  • --End

  • 我们看上面的B和C列都有我们所说的NULL出现了,好我们先通过UNPIVOT来把行列转换一下看看结果:
  • ------------------------------------
  • --Author:happyflsytone
  • --Date:2008-09-2214:05:26
  • ------------------------------------

  • --TestData:ta
  • IFOBJECT_ID('ta')ISNOTNULL
  • DROPTABLEta
  • Go
  • CREATETABLEta(col1nvarchar(2),col2nvarchar(2),Anvarchar(1),Bnvarchar(1),Cnvarchar(1))
  • Go
  • INSERTINTOta
  • select'HN','CD','0',null,'0'unionall
  • select'HN','CS','1','2',nullunionall
  • select'HN','HY','0','0','4'
  • GO
  • SELECTcol1,col2,CAST([NEWCOL]ASVARCHAR(2))AS[NEWCOL],[NEWCOLV]
  • FROM
  • (SELECTcol1,col2,A,B,C
  • FROMta)p
  • UNPIVOT
  • (NEWCOLV
  • FORNEWCOLIN(A,B,C)
  • )ASunpvt


  • /*
  • col1col2NEWCOLNEWCOLV
  • ---------------------
  • HNCDA0
  • HNCDC0
  • HNCSA1
  • HNCSB2
  • HNHYA0
  • HNHYB0
  • HNHYC4

  • (7行受影响)
  • */
  • 很显然转换后的最终结果和我们一起的相比发现少了两行,这两行就是一开始列值有NULL的记录,这就是UNPIVOT的一个特殊的地方:UNPIVOT的输入中的NULL不会显示在输出中。大家一定要注意这一点。

  • 最后,我们对前一讲的PIVOT和现在这个UNPIVOT进行一个总结,我们说UNPIVOT几乎是PIVOT的的反操作,并不完全是PIVOT的逆操作,为什么说不完全是?刚才上面这个先PIVOT再UNPIVOT后的记录忽略了NULL的情况首先就能说明不完全是反操作,下面我们再通过另一个例程说这个不完全:

  • ------------------------------------
  • --Author:happyflsytone
  • --Date:2008-09-2214:33:20
  • ------------------------------------

  • --TestData:ta
  • IFOBJECT_ID('ta')ISNOTNULL
  • DROPTABLEta
  • Go
  • CREATETABLEta(col1nvarchar(2),col2nvarchar(2),NEWCOLnvarchar(1),NEWCOLVint)
  • Go
  • INSERTINTOta
  • select'HN','CD','A',0unionall
  • select'HN','CD','B',2unionall
  • select'HN','CD','C',0unionall
  • select'HN','CD','C',5unionall
  • select'HN','CS','A',1unionall
  • select'HN','CS','B',2unionall
  • select'HN','CS','B',2unionall
  • select'HN','CS','C',4unionall
  • select'HN','HY','A',0unionall
  • select'HN','HY','A',9unionall
  • select'HN','HY','B',0unionall
  • select'HN','HY','C',4
  • GO
  • --Start
  • PRINT'--------------原始数据----------------------------'
  • SELECT*
  • FROMTA

  • SELECTcol1,col2,[A],[B],[C]
  • FROM
  • (SELECTCOL1,COL2,newcolv,newcol
  • FROMTA)P
  • PIVOT
  • (SUM(NEWCOLV)
  • FORNEWCOLIN([A],[B],[C])
  • )ASunpvt
  • --Result:
  • /*
  • col1col2ABC
  • -----------------------------------------
  • HNCD025
  • HNCS144
  • HNHY904

  • (3行受影响)

  • */
  • --End
  • 我们接着把上面的结果作为原始数据进行列转成行,
  • --TestData:ta
  • IFOBJECT_ID('ta')ISNOTNULL
  • DROPTABLEta
  • Go
  • CREATETABLEta(col1NVARCHAR(2),col2NVARCHAR(2),AINT,BINT,CINT)
  • Go
  • INSERTINTOta
  • SELECT'HN','CD','0',2,5UNIONALL
  • SELECT'HN','CS','1',4,4UNIONALL
  • SELECT'HN','HY','9',0,4
  • GO
  • --Start
  • PRINT'--------------还原的原始数据----------------------------'
  • SELECTcol1,col2,CAST([NEWCOL]ASVARCHAR(2))AS[NEWCOL],[NEWCOLV]
  • FROM
  • (SELECTcol1,col2,A,B,C
  • FROMta)p
  • UNPIVOT
  • (NEWCOLV
  • FORNEWCOLIN(A,B,C)
  • )ASunpvt
  • /*
  • --------------原始数据----------------------------
  • col1col2NEWCOLNEWCOLV
  • -------------------------
  • HNCDA0
  • HNCDB2
  • HNCDC0
  • HNCDC5
  • HNCSA1
  • HNCSB2
  • HNCSB2
  • HNCSC4
  • HNHYA0
  • HNHYA9
  • HNHYB0
  • HNHYC4

  • (12行受影响)

  • --------------还原的原始数据----------------------------
  • col1col2NEWCOLNEWCOLV
  • -------------------------
  • HNCDA0
  • HNCDB2
  • HNCDC5
  • HNCSA1
  • HNCSB4
  • HNCSC4
  • HNHYA9
  • HNHYB0
  • HNHYC4

  • (9行受影响)

  • */
  • 通过原始数据及还原的原始数据对比,我们发现UNPIVT是无法反操作PIVOT操作时运用聚合函数生成的新列的情况,所以我们小结如下:
  • 1、如果PIVOT中使用聚合函数,则计算聚合时将不考虑出现在值列中的任何NULL值;
  • 2、UNPIVOT的输入中的NULL不会显示在输出中;
  • 3、UNPIVOT的输出会无法完全还原PIVOT操作之前输入中可能会含有原始的NULL值;
  • 4、UNPIVOT的输出会无法完全还原PIVOT操作之前输入中各行的明细值(因为用了聚合函数);

  • 好,我们对2005的UNPIVOT及PIVOT这个新功能的学习就到这儿了。



  • 无枪狙击手
  • 于宁.一个风雨交加的夜晚

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-295982-1-1.html 上篇帖子: 一个struts+sql server的分页存储过程 下篇帖子: SQL SERVER乐观锁定和悲观锁定使用实例
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表