oracle-行转列点评oracle11g sql新功能pivot/unpivot
http://hi.baidu.com/yyfangzong/item/01e5bbe8323d180965db00ed 摘要:(简要介绍Oracle11g SQL的新功能 pivot/unpivot 的使用方法以及如何使用它们做到行列转换. 蓄势以久的Oracle 11g 终于七月敲锣打鼓隆重推出,接下来就是网上漫天盖地的新功能介绍。11g面向开发的新功能本来就不多,掰着手指头也就是pivot和查询结果缓存的新Hint。本以为不久就会有人详述,谁知盼到两眼欲穿,大家还是翻来覆去的讨论DBA的自动分区之类。Oracle自己的门脸上到是每每用客气的冷漠写着“马上就来” (coming soon),可这马上都转眼都快马上了一个月了,还迟迟不见盖头掀起来。 (http://www.oracle.com/technology/pub/articles/oracle-database-11g-top-features/index.html?rssid=rss_otn_articles) 1. 11g以前的行列转换 假设有表emp_phone如下: 写这个SQL的技巧就是按姓名分组,然后使每一组每一类的电话号码最多只有一个,里边用到的分组函数都是聋子的耳朵-摆设。用MAX可以,MIN也行。 这个查询写出来就是: 那位看官说了:“能不能再变回去?”能,不能戏法不就漏了不是? SELECT 转来转去,一来一往,阴阳辟易,详推用意终何在,延年益寿不老春。往玄里说,就是老祖宗老挂在嘴边上的“道”。那位又说了:“这都哪儿跟哪儿啊?怎么扯到太极拳上去了”。 11g在SELECT语句中新加了关键词PIVOT和UNPIVOT,用这两个关键词,重写上面的两个查询,就变成这个样子的了: 行变列: PIVOT以后的字句都是新加的。但万变不离其宗,还是要用到分组函数。IN后边是按type的不同值映射成不同的列。简单吧? 列变行,这是UNPIVOT的工作,写法如下: SELECT * FROM emp_phone_x UNPIVOT ( phone FOR type in (HOME AS 1,OFFICE AS 2,MOBILE AS 3) ) / 这里是把不同的列转换成不同的type的数值。再用SCOTT用户里的EMP表做个例子,列出各部门之间工资总和: SELECT * FROM ( (SELECT sal,deptno FROM emp) PIVOT ( SUM(sal) FOR deptno IN (10 as dept_10,20 as dept_20,30 as dept_30) ) ) / DEPT_10 DEPT_20 DEPT_30 ---------- ---------- ---------- 8750 10875 9400 再往深里想,前边的所有例子都有一个局限,电话的type和emp的deptno都是有限的、可穷举的。如果这些列都是可随时可添加的,又该怎么办呢?11g以前肯定是要动用动态SQL的法宝。那11g又是怎么处理的呢?刚看SQL参考手册的时候,看到里边豁然写着IN后边可以接子查询或ANY,当时是佩服的眼泪哗哗的,迫不及待赶紧试一试: SELECT * FROM 这一下又变成拔凉拔凉的,这么大个ORACLE也不能无耻到这个地步吧?正准备再确认一下手册,抓他个人赃俱获,突然有发现里边豁然写着: A subquery is used only in conjunction with the XML keyword… The ANY keyword is used only in conjunction with the XML keyword… 学习不认真,该打。原来是给生成XML串用的,正确用法如下: SELECT * FROM ( (SELECT sal,deptno FROM emp) PIVOT XML ( SUM(sal) FOR deptno IN (ANY) ) ) <PivotSet><item><column name = "DEPTNO">10</column><column name = "SUM(SAL)">8750</column></item><item><column name = "DEPTNO">20</column><column name = "SUM(SAL)">10875</column></item><item><column name = "DEPTNO">30</column><column name = "SUM(SAL)">9400</column></item></PivotSet> 2012-11-2922:51:21 我自己的测试
注意:建表插入的时候varchar字符型必须要加上'', insert into s1 values('yuan',english80);
|