今天在javaeye上看到一道面试题,很多人都用over函数解决的
特意查了一下它的用法
sql> select deptno,ename,sal
2 from emp
3 order by deptno;
DEPTNO ENAME SAL
---------- ---------- ----------
10 CLARK 2450
KING 5000
MILLER 1300
20 SMITH 800
ADAMS 1100
FORD 3000
SCOTT 3000
JONES 2975
30 ALLEN 1600
BLAKE 2850
MARTIN 1250
JAMES 950
TURNER 1500
WARD 1250
已选择14行。
2.先来一个简单的,注意over(...)条件的不同,使用sum(sal) over(order by ename)...查询员工的薪水“连续”求和,
注意over(order by ename)如果没有order by子句,求和就不是的,放在一起,体会一下不同之处:
sql> select deptno,sal,
2 sum(sal) over (order by ename)连续求和总和此处sum(sal) over ()等同于sum(sal)
4 100*round(sal/sum(sal) over (),4) "份额(%)"
5 from emp
6 /
DEPTNO ENAME SAL(%)
---------- ---------- ---------- ---------- ---------- ----------
20 ADAMS 1100 1100 29025 3.79
30 ALLEN 1600 2700 29025 5.51
30 BLAKE 2850 5550 29025 9.82
10 CLARK 2450 8000 29025 8.44
20 FORD 3000 11000 29025 10.34
30 JAMES 950 11950 29025 3.27
20 JONES 2975 14925 29025 10.25
10 KING 5000 19925 29025 17.23
30 MARTIN 1250 21175 29025 4.31
10 MILLER 1300 22475 29025 4.48
20 SCOTT 3000 25475 29025 10.34
20 SMITH 800 26275 29025 2.76
30 TURNER 1500 27775 29025 5.17
30 WARD 1250 29025 29025 4.31
3.使用子分区查出各部门薪水连续的总和。注意按部门分区。注意
sum(sal) over (partition by deptno order by ename)按部门求总和
sum(sal) over (partition by deptno)按部门求总和
sum(sal) over (order by deptno,ename)不按部门
sum(sal) over ()不按部门,求所有员工总和,效果等同于sum(sal)。部门连续求和各部门的薪水"
3 sum(sal) over (partition by deptno)部门总和部门统计的总和,同一部门总和不变
4 100*round(sal/sum(sal) over (partition by deptno),4)"部门份额(%)",
5 sum(sal) over (order by deptno,ename)所有部门的薪水
6 sum(sal) over (),所有员工的薪水总和
7 100*round(sal/sum(sal) over (),75)">总份额(%)"
8 from emp
9 /
DEPTNO ENAME SAL(%)(%)
------ ------ ----- ------------ ---------- ----------- ---------- ----------------
10 CLARK 2450 2450 8750 28 2450 29025 8.44
KING 5000 7450 8750 57.14 7450 29025 17.23
MILLER 1300 8750 8750 14.86 8750 29025 4.48
20 ADAMS 1100 1100 10875 10.11 9850 29025 3.79
FORD 3000 4100 10875 27.59 12850 29025 10.34
JONES 2975 7075 10875 27.36 15825 29025 10.25
SCOTT 3000 10075 10875 27.59 18825 29025 10.34
SMITH 800 10875 10875 7.36 19625 29025 2.76
30 ALLEN 1600 1600 9400 17.02 21225 29025 5.51
BLAKE 2850 4450 9400 30.32 24075 29025 9.82
JAMES 950 5400 9400 10.11 25025 29025 3.27
MARTIN 1250 6650 9400 13.3 26275 29025 4.31
TURNER 1500 8150 9400 15.96 27775 29025 5.17
WARD 1250 9400 9400 13.3 29025 29025 4.31
4.来一个综合的例子,求和规则有按部门分区的,有不分区的例子
5.来一个逆序的,即部门从大到小排列,部门里各员工的薪水从高到低排列,累计和的规则不变。
6.体会:在"... from emp;"后面不要加orderby子句,使用的分析函数的(partition by deptno order by sal)
里已经有排序的语句了,如果再在句尾添加排序子句,一致倒罢了,不一致,结果就令人费劲了。如:行
row_number() over ([partition by col1] order by col2) )as别名
表示根据col1分组,在分组内部根据 col2排序
而这个“别名”的值就表示每组内部排序后的顺序编号(组内连续的唯一的),[partition by col1] 可省略。
以Scott/tiger登陆,以emp表为例。
1、select deptno,
sum(sal) over (order by ename) 累计,--按姓名排序,并将薪水逐个累加
sum(sal) over () 总和,-- 此处sum(sal) over () 等同于sum(sal),求薪水总和
100*round(sal/sum(sal) over (),4) "份额(%)" --求每个人的薪水占总额的比例,小数点后保留2位,括号和百分号为特殊符号,所以需要“”
from emp
结果如下 :
2、select deptno,
sum(sal) over (partition by deptno order by ename) 部门连续求和,--partition by deptno先按部门分组,再按姓名排序,并将薪水逐个累加
sum(sal) over (partition by deptno) 部门总和,-- 每个部门的薪水总和
100*round(sal/sum(sal) over (partition by deptno),4) "部门份额(%)",--每个员工在各自部门的薪水比例
sum(sal) over (order by deptno,ename) 连续求和,--所有部门的薪水"连续"求和
sum(sal) over () 总和,-- 此处sum(sal) over () 等同于sum(sal),所有员工的薪水总和
100*round(sal/sum(sal) over (),4) "总份额(%)" --求每个人的薪水占总额的比例
from emp
结果如下:
3、select deptno,sum(sal)over (partition by deptno order by sal) dept_sum,--根据部门分组,再按部门内的个人薪水排序,逐个累加。
sum(sal) over (order by deptno,sal) sum --按部门排序,将薪水逐个累加。
from emp;
4、部门从大到小排列,部门里各员工的薪水从高到低排列
select deptno,
sum(sal) over (partition by deptno order by deptno desc,sal desc)dept_sum,--按部门分组后,按部门和薪水降序排
sum(sal) over (order by deptno desc,sal desc) sum --按部门和薪水降序排
from emp;
5、将各部门的员工按薪水排序
select ename,job,deptno,(row_number() over(partition by deptno orderby sal desc)) as 排名 --先按部门分组,再在部门中按薪水降序排名
from scott.emp
6、查找各部门中薪水最高的前2位
7、如果已经在over()中进行过分组,在"... from emp;"后面不要加orderby 子句。
step 3 利用sys_connect_by_path生成结果集:
SELECTcountry,sys_connect_by_path(city,',') city
FROM
(SELECT country,city,country||
rn rchild,country||(rn-1) rfather
FROM
(SELECTtest.country,test.city,row_number()
over (PARTITION BY test.country ORDER BY
test.city) rn
FROM test))
CONNECT BY PRIOR rchild=rfather START WITH rfather LIKE '%0'
日本,大阪
东京中国上海台北香港
step 4 最终步骤,筛选结果集合:
sql>SELECT country,max(substr(city,2)) city
23(SELECTcountry,') city
45678FROMtest))
9CONNECTBY PRIOR rchild=rfather START WITH rfather LIKE '%0')
10GROUP BY country;
COUNTRYCITY
-------------------- -------
中国 香港日本 东京
怎么合并多行记录的字符串,我同事曾经建议我用FUNCTION来实现,但是我的报表中有多个类似的字段需要合并,因此我想用最简单的方法来操作,没接受此方案,今天在网上看到这样一篇文章,实在是太高兴了,而且还使用到了partition和聚集,这样更能丰富合并多行记录的处理方法.
下面就合并多行字符串贴出刚刚在网上搜到的一个好贴!
什么是合并多行字符串(连接字符串)呢,例如:
sql>desctest;
NameTypeNullableDefaultComments
------------------------------------------
COUNTRYVARCHAR2(20)Y
CITYVARCHAR2(20)Y
sql>select*fromtest;
COUNTRYCITY
----------------------------------------
中国台北
中国香港
中国上海
日本东京
日本大阪
要求得到如下结果集:
---------------------------
中国台北,香港,上海
日本东京,大阪
实际就是对字符实现一个聚合功能,我很奇怪为什么Oracle没有提供官方的聚合函数来实现它呢:) 下面就对几种经常提及的解决方案进行分析(有一个评测标准最高★★★★★): 1.被集合字段范围小且固定型灵活性★性能★★★★难度★ 这种方法的原理在于你已经知道CITY字段的值有几种,且还不算太多,如果太多这个sql就会相当的 长。。看例子: sql>selectt.country,2MAX(decode(t.city,'台北',t.city||',NULL))|| 3MAX(decode(t.city,'香港',NULL))|| 4MAX(decode(t.city,'上海',NULL))|| 5MAX(decode(t.city,'东京',NULL))|| 6MAX(decode(t.city,'大阪',NULL)) 7fromtesttGROUPBYt.country 8/ COUNTRYMAX(DECODE(T.CITY,T.CIT -------------------------------------------------- 中国台北,上海,日本东京,大阪,大家一看,估计就明白了(如果不明白,好好补习MAXDECODE和分组)。这种方法无愧为最笨的方法 ,但是对某些应用来说,最有效的方法也许就是它。 2.固定表固定字段函数法灵活性★★性能★★★★难度★★ 此法必须预先知道是哪个表,也就是说一个表就得写一个函数,不过方法1的一个取值就要便捷多了。在大多数应用中,也不会存在大量这种合并字符串的需求。废话完毕,看下面: 定义一个函数 createorreplacefunctionstr_list(str_ininvarchar2)--分类字段 returnvarchar2 is str_listvarchar2(4000)defaultnull;--连接后字符串 strvarchar2(20)defaultnull;--连接符号 begin forxin(selectTEST.CITYfromTESTwhereTEST.COUNTRY=str_in)loop str_list:=str_list||str||to_char(x.city); str:=','; endloop; returnstr_list; end; 使用: sql>selectDISTINCT(T.country),list_func1(t.country)fromtestt; COUNTRYLIST_FUNC1(T.COUNTRY) ------------------------------------ 中国台北,上海 日本东京,大阪 sql>selectt.country,str_list(t.country)fromtesttGROUPBYt.country; COUNTRYSTR_LIST(T.COUNTRY) ------------------------------------------- 中国台北,大阪 这个时候,使用分组和求唯一都可以满足要求。它的原理就是,根据唯一的分组字段country,在函数里面再次查询该字段对应的所有被合并列,使用PL/sql将其合并输出。 3.灵活表函数法灵活性★★★性能★★★难度★★★ 该方法是在方法2的基础上,使用动态sql,将表名和字段名称传入,从而达到灵活的目的。 createorreplacefunctionstr_list2(key_nameinvarchar2,keyinvarchar2,conameinvarchar2,tnameinvarchar2) returnvarchar2 as typercisrefcursor; strvarchar2(4000); sepvarchar2(2); valvarchar2(4000); currc; begin opencurfor'select'||coname||' from'||tname||' where'||key_name||'=:x' usingkey; loop fetchcurintoval; exitwhencur%notfound; str:=str||sep||val; sep:=','; endloop; closecur; returnstr; end; sql>selecttest.country,2str_list2('COUNTRY',test.country,'CITY','TEST')emplist 3fromtest 4groupbytest.country 5/ COUNTRYEMPLIST ------------------------------------- 中国台北,大阪 4.一条sql法灵活性★★★★性能★★难度★★★★ 一条sql的法则是某位大师提出的,大家曾经在某个时期都乐此不彼的寻求各种的问题一条sql法,但是大师的意思似乎被曲解,很多性能差,可读性差,灵活差的sql都是这个原则产物,所谓画虎不成反成犬类。不过,解决问题始终是第一原则,这里还是给出一个比较有代表性的一条sql方法。 SELECTcountry,2))city FROM (SELECTcountry,')city FROM (SELECTcountry,country||rnrchild,country||(rn-1)rfather FROM (SELECTtest.country,row_number()over(PARTITIONBYtest.countryORDERBY test.city)rn FROMtest)) CONNECTBYPRIORrchild=rfatherSTARTWITHrfatherLIKE'%0') GROUPBYcountry; 下面分步解析,有4个FROM,就有4次结果集的操作。 step1给记录加上序号rn sql>SELECTtest.country,row_number()over(PARTITIONBYtest.countryORDER BYtest.city)rn 2FROMtest 3/ COUNTRYCITYRN -------------------------------------------------- 日本大阪1 日本东京2 中国上海1 中国台北2 中国香港3 step2创造子节点父节点 sql>SELECTcountry,country||(rn-1)rfather 2FROM 3(SELECTtest.country,row_number()over(PARTITIONBYtest.countryORDER BYtest.city)rn 4FROMtest) 5/ 日本大阪日本1日本0 日本东京日本2日本1 中国上海中国1中国0 中国台北中国2中国1 中国香港中国3中国2 step3利用sys_connect_by_path生成结果集 SELECTcountry,row_number()over(PARTITIONBYtest.countryORDERBY test.city)rn FROMtest)) CONNECTBYPRIORrchild=rfatherSTARTWITHrfatherLIKE'%0' 日本,大阪 日本,东京 中国,上海 中国,台北 中国,台北,香港 step4最终步骤,筛选结果集合 sql>SELECTcountry,2))city 2FROM 3(SELECTcountry,')city 4FROM 5(SELECTcountry,country||(rn-1)rfather 6FROM 7(SELECTtest.country,row_number()over(PARTITIONBYtest.countryORDER BYtest.city)rn 8FROMtest)) 9CONNECTBYPRIORrchild=rfatherSTARTWITHrfatherLIKE'%0') 10GROUPBYcountry; COUNTRYCITY --------------------------- 中国上海,香港 日本大阪,东京 可谓是,7歪8搞,最后还是弄出来了,呵呵。 5.自定义聚合函数灵活性★★★★★性能★★★★★难度★★★★★ 最后一个方法是我认为“王道”的方法,自定义聚合函数。 就如何我在本开始说的,为啥oracle没有这种聚合函数呢?我也不知道,但oracle提供了聚合函数的 API可以让我方便的自己定义聚合函数。 详细可以看OracleDataCatridgeguide这个文档。连接如下: http://www.oracle.com.cn/other/9ionlinedoc/appdev.920/a96595/toc.htm 下面给出一个简单的例子: sql>SELECTt.country,strcat(t.city)FROMtesttGROUPBYt.country; COUNTRYSTRCAT(T.CITY) -------------------------------------- 日本东京,大阪 中国台北,上海 简单吧,和官方的函数一样的便捷高效。 函数: CREATEORREPLACEFUNCTIONstrcat(inputvarchar2) RETURNvarchar2 PARALLEL_ENABLEAGGREGATEUSINGstrcat_type; TYPE: createorreplacetypestrcat_typeasobject( cat_stringvarchar2(4000),staticfunctionODCIAggregateInitialize(cs_ctxInOutstrcat_type)returnnumber,memberfunctionODCIAggregateIterate(selfInOutstrcat_type,valueinvarchar2)return number,memberfunctionODCIAggregateMerge(selfInOutstrcat_type,ctx2InOutstrcat_type) returnnumber,memberfunctionODCIAggregateTerminate(selfInOutstrcat_type,returnValueOut varchar2,flagsinnumber)returnnumber )