关于Oracle分组后字段拼接的问题

转自: CSDN社区
原文地址: http://topic.csdn.net/t/20050722/11/4161213.html
duanzilin(寻)2005-07-22 11:52:55 在 Oracle / 基础和管理 提问

最近在论坛上,经常会看到关于分组后字段拼接的问题,  
  大概是类似下列的情形:  


  SQL>   select   no,q   from   test  
      2     /  
   
  NO                   Q  
  ----------   ------------------------------  
  001                 n1  
  001                 n2  
  001                 n3  
  001                 n4  
  001                 n5  
  002                 m1  
  003                 t1  
  003                 t2  
  003                 t3  
  003                 t4  
  003                 t5  
  003                 t6  
   
  12   rows   selected  
   
  最后要得到类似于如下的结果:  
  001                 n1;n2;n3;n4;n5  
  002                 m1  
  003                 t1;t2;t3;t4;t5;t6  
   
      通常大家都认为这类问题无法用一句SQL解决,本来我也这么认为,可是今天无意中突然有了灵感,原来是可以这么做的:  
      前几天有人提到过sys_connect_by_path的用法,我想这里是不是也能用到这个方法,如果能做到的话,不用函数或存贮过程也可以做到了;要用到sys_connect_by_path,首先要自己构建树型的结构,并且树的每个分支都是单根的,例如1-〉2-〉3-〉4,不会存在1-〉2,1-〉3的情况;  
      我是这么构建树,很简单的,看下面的结果就会知道了:  
  SQL>   select   no,q,rn,lead(rn)   over(partition   by   no   order   by   rn)   rn1  
      2     from   (select   no,q,row_number()   over(order   by   no,q   desc)   rn   from   test)  
      3     /  
   
  NO                   Q                                                                             RN                 RN1  
  ----------   ------------------------------   ----------   ----------  
  001                 n5                                                                             1                     2  
  001                 n4                                                                             2                     3  
  001                 n3                                                                             3                     4  
  001                 n2                                                                             4                     5  
  001                 n1                                                                             5    
  002                 m1                                                                             6    
  003                 t6                                                                             7                     8  
  003                 t5                                                                             8                     9  
  003                 t4                                                                             9                   10  
  003                 t3                                                                           10                   11  
  003                 t2                                                                           11                   12  
  003                 t1                                                                           12    
   
  12   rows   selected  
   
  有了这个树型的结构,接下来的事就好办了,只要取出拥有全路径的那个path,问题就解决了,先看no=‘001’的分组:  
  select   no,sys_connect_by_path(q,';')   result   from    
                (select   no,q,rn,lead(rn)   over(partition   by   no   order   by   rn)   rn1    
                from   (select   no,q,row_number()   over(order   by   no,q   desc)   rn   from   test)  
                )  
  start   with   no   =   '001'   and   rn1   is   null   connect   by   rn1   =   prior   rn  
  SQL>    
      6     /  
   
  NO                   RESULT  
  ----------   --------------------------------------------------------------------------------  
  001                 ;n1  
  001                 ;n1;n2  
  001                 ;n1;n2;n3  
  001                 ;n1;n2;n3;n4  
  001                 ;n1;n2;n3;n4;n5  
   
  上面结果的最后1条就是我们要得结果了  
  要得到每组的结果,可以下面这样  
   
  select   t.*,  
                (  
                  select   max(sys_connect_by_path(q,';'))   result   from    
                                (select   no,q,rn,lead(rn)   over(partition   by   no   order   by   rn)   rn1    
                                from   (select   no,q,row_number()   over(order   by   no,q   desc)   rn   from   test)  
                                )  
                  start   with   no   =   t.no   and   rn1   is   null   connect   by   rn1   =   prior   rn  
                )   value  
  from   (select   distinct   no   from   test)     t  
   
  SQL>    
    10     /  
   
  NO                   VALUE  
  ----------   --------------------------------------------------------------------------------  
  001                 ;n1;n2;n3;n4;n5  
  002                 ;m1  
  003                 ;t1;t2;t3;t4;t5;t6  
   
  对上面结果稍加处理就可以了,希望对大家有帮助:) 问题点数:200、回复次数:29Top

UandM(NULL)回复于 2005-07-22 12:41:37 得分 0

select   t.*,  
                (  
                  select   max(sys_connect_by_path(q,';'))   result   from    
                                (select   no,q,rn,lead(rn)   over(partition   by   no   order   by   rn)   rn1    
                                from   (select   no,q,row_number()   over(order   by   no,q   desc)   rn   from   test)  
                                )  
                  start   with   no   =   t.no   and   rn1   is   null   connect   by   rn1   =   prior   rn  
                )   value  
  from   (select   distinct   no   from   test)     t  
  可以简化一下:  
  select   no,max(sys_connect_by_path(q,';'))   result   from    
  (  
      select   no,q,rn,lead(rn)   over(partition   by   no   order   by   rn)   rn1    
        from   (  
                      select   no,q,row_number()   over(order   by   no,q   desc)   rn   from   test  
                  )  
    )  
    connect   by   rn1   =   prior   rn  
    group   by   no  
  Top

7 楼duanzilin(寻)回复于 2005-07-22 12:51:00 得分 0

 
    to   UandM(无),你的方法不行,关键是start   with   的问题,你可以再研究下我这么写的用意,还是谢谢你了,大家有什么意见可以踊跃提出  
   
   
  select   no,max(sys_connect_by_path(q,';'))   result   from    
  (  
      select   no,q,rn,lead(rn)   over(partition   by   no   order   by   rn)   rn1    
        from   (  
                      select   no,q,row_number()   over(order   by   no,q   desc)   rn   from   test  
                  )  
    )  
    connect   by   rn1   =   prior   rn  
    group   by   no  
   
  SQL>    
    10     /  
   
  NO                   RESULT  
  ----------   --------------------------------------------------------------------------------  
  001                 ;n5  
  002                 ;m1  
  003                 ;t6  
   
  SQL>    
   
   
  Top

9 楼UandM(NULL)回复于 2005-07-22 13:09:24 得分 28

select   no,max(sys_connect_by_path(q,';'))   result   from    
  (  
      select   no,q,rn,lead(rn)   over(partition   by   no   order   by   rn)   rn1    
        from   (  
                      select   no,q,row_number()   over(order   by   no,q   desc)   rn   from   test  
                  )  
    )  
    start   with   rn1   is   null     ---呵呵,确实,这句不能少  
    connect   by   rn1   =   prior   rn  
    group   by   no  
   
  Top

11 楼nowait(独行天涯路)回复于 2005-07-22 13:14:14 得分 10

我始终认为可以一条sql解决:  
  SQL>   select   *   from   test_lead;  
   
  N       Q  
  ---   --  
  001   n1  
  001   n2  
  001   n3  
  001   n4  
  001   n5  
  002   m1  
  003   t1  
  003   t2  
  003   t3  
  003   t4  
  003   t5  
  003   t6  
   
  12   行   已选择  
   
  SQL>    
  SQL>   select   n,rtrim(q,';0')   q   from   (  
      2     select   n   ,q,row_number()over(partition   by   n   order   by   n)   as   r   from   (  
      3     select   n0   n,q0||';'||q1||';'||q2||';'||q3||';'||q4||';'||q5   as   q     from   (  
      4     select   n   n0,q   q0,  
      5                   lead(q,1,0)over(partition   by   n   order   by   q)   q1,  
      6                   lead(q,2,0)over(partition   by   n     order   by   q)   q2,  
      7                                 lead(q,3,0)over(partition   by   n     order   by   q)   q3,  
      8                                               lead(q,4,0)over(partition   by   n     order   by   q)   q4,  
      9                                                             lead(q,5,0)over(partition   by   n     order   by   q)   q5  
    10                                 from   test_lead))  
    11                               )   where   r=1;  
   
  N       Q  
  ---   --------------------------------------------------------------------------------  
  001   n1;n2;n3;n4;n5  
  002   m1  
  003   t1;t2;t3;t4;t5;t6  
   
  SQL>Top

12 楼UandM(NULL)回复于 2005-07-22 13:22:29 得分 0

nowait(独行天涯路)   ,  
  --如果N有50多种,你要写到多长?  
  Top

13 楼duanzilin(寻)回复于 2005-07-22 13:40:13 得分 0

UandM(无)   正解,我的代码也改了N次了,很多写法都是继承最开始时的思路,谢谢你的建议:  
   
  select   no,max(sys_connect_by_path(q,';'))   result   from    
  (  
      select   no,q,rn,lead(rn)   over(partition   by   no   order   by   rn)   rn1    
        from   (  
                      select   no,q,row_number()   over(order   by   no,q   desc)   rn   from   test  
                  )  
    )  
    start   with   rn1   is   null    
    connect   by   rn1   =   prior   rn  
    group   by   no  
   
  SQL>    
    11     /  
   
  NO                   RESULT  
  ----------   --------------------------------------------------------------------------------  
  001                 ;n1;n2;n3;n4;n5  
  002                 ;m1  
  003                 ;t1;t2;t3;t4;t5;t6  
  Top

14 楼bzszp(SongZip)回复于 2005-07-22 15:15:18 得分 10

分析函数确实功能强大!!Top

15 楼bzszp(SongZip)回复于 2005-07-22 15:25:50 得分 0

8i里面当时看了一下分析函数的文档,分析函数还没有几个。  
  9i里面加了不少  
  哪位兄弟有时间整理一下,加入精华区里面。  
  Top

17 楼duanzilin(寻)回复于 2005-08-01 11:22:52 得分 0

改进下算法,少一层嵌套查询,效率会好些  
  select   no,max(sys_connect_by_path(q,';'))   result   from    
  (  
                select   no,q,(row_number()   over(order   by   no,q   desc)   +   rank()   over(order   by   no))   rn  
                from   test  
  )  
    connect   by   rn-1   =   prior   rn  
    group   by   no  
  Top

18 楼duanzilin(寻)回复于 2005-08-01 13:13:59 得分 0

上面的算法有点问题,下面的是正确的:  
  select   no,max(sys_connect_by_path(q,';'))   result    
  from   (select   no,q,  
                            (row_number()   over(order   by   no,q   desc)    
                            +   dense_rank()   over(order   by   no))   rn,    
                            max(q)   over(partition   by   no)   qs  
              from   test  
  )  
    start   with   q   =   qs  
    connect   by   rn-1   =   prior   rn  
    group   by   noTop

 

23 楼waterfirer(水清)回复于 2005-08-01 16:10:55 得分 20

 

duanzilin(寻)   后面的算法确实简单啊,rn相差1的就是一组的,相差2的就不是一组的。  
   
      mark。Top

24 楼waterfirer(水清)回复于 2005-08-01 16:22:45 得分 0

to   bzszp(SongZip):说我回复内容太长,只好分开回复了。  
   
  Oracle   9i   分析函数参考手册  
   
                  Oracle从8.1.6开始提供分析函数,分析函数用于计算基于组的某种聚合值,它和聚合函数的不同之处是对于每个组返回多行,而聚合函数对于每个组只返回一行。  
  下面例子中使用的表来自Oracle自带的HR用户下的表,如果没有安装该用户,可以在SYS用户下运行$ORACLE_HOME/demo/schema/human_resources/hr_main.sql来创建。  
                  少数几个例子需要访问SH用户下的表,如果没有安装该用户,可以在SYS用户下运行$ORACLE_HOME/demo/schema/sales_history/sh_main.sql来创建。  
                  如果未指明缺省是在HR用户下运行例子。  
                  开窗函数的的理解:  
                  开窗函数指定了分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化,举例如下:  
  over(order   by   salary)   按照salary排序进行累计,order   by是个默认的开窗函数  
  over(partition   by   deptno)按照部门分区  
  over(order   by   salary   range   between   50   preceding   and   150   following)  
  每行对应的数据窗口是之前行幅度值不超过50,之后行幅度值不超过150  
  over(order   by   salary   rows   between   50   preceding   and   150   following)  
  每行对应的数据窗口是之前50行,之后150行  
  over(order   by   salary   rows   between   unbounded   preceding   and   unbounded   following)  
  每行对应的数据窗口是从第一行到最后一行,等效:  
  over(order   by   salary   range   between   unbounded   preceding   and   unbounded   following)  
   
  主要参考资料:《expert   one-on-one》   Tom   Kyte     《Oracle9i   SQL   Reference》第6章  
   
   
  AVG    
  功能描述:用于计算一个组和数据窗口内表达式的平均值。  
  SAMPLE:下面的例子中列c_mavg计算员工表中每个员工的平均薪水报告,该平均值由当前员工和与之具有相同经理的前一个和后一个三者的平均数得来;  
   
  SELECT   manager_id,   last_name,   hire_date,   salary,  
        AVG(salary)   OVER   (PARTITION   BY   manager_id   ORDER   BY   hire_date    
        ROWS   BETWEEN   1   PRECEDING   AND   1   FOLLOWING)   AS   c_mavg  
        FROM   employees;  
   
  MANAGER_ID   LAST_NAME                                   HIRE_DATE           SALARY           C_MAVG  
  ----------   -------------------------   ---------   ----------   ----------  
                100   Kochhar                                       21-SEP-89             17000             17000  
                100   De   Haan                                       13-JAN-93             17000             15000  
                100   Raphaely                                     07-DEC-94             11000   11966.6667  
                100   Kaufling                                     01-MAY-95               7900   10633.3333  
                100   Hartstein                                   17-FEB-96             13000   9633.33333  
                100   Weiss                                           18-JUL-96               8000   11666.6667  
                100   Russell                                       01-OCT-96             14000   11833.3333  
  .  
  .  
  .  
   
   
  CORR    
  功能描述:返回一对表达式的相关系数,它是如下的缩写:  
                      COVAR_POP(expr1,expr2)/STDDEV_POP(expr1)*STDDEV_POP(expr2))  
                      从统计上讲,相关性是变量之间关联的强度,变量之间的关联意味着在某种程度  
                      上一个变量的值可由其它的值进行预测。通过返回一个-1~1之间的一个数,   相关  
                      系数给出了关联的强度,0表示不相关。  
  SAMPLE:下例返回1998年月销售收入和月单位销售的关系的累积系数(本例在SH用户下运行)  
   
  SELECT   t.calendar_month_number,  
                CORR   (SUM(s.amount_sold),   SUM(s.quantity_sold))  
                OVER   (ORDER   BY   t.calendar_month_number)   as   CUM_CORR  
      FROM   sales   s,   times   t  
  WHERE   s.time_id   =   t.time_id   AND   calendar_year   =   1998  
  GROUP   BY   t.calendar_month_number  
  ORDER   BY   t.calendar_month_number;  
   
  CALENDAR_MONTH_NUMBER       CUM_CORR  
  ---------------------   ----------  
                                          1  
                                          2                     1  
                                          3   .994309382  
                                          4   .852040875  
                                          5   .846652204  
                                          6   .871250628  
                                          7   .910029803  
                                          8   .917556399  
                                          9   .920154356  
                                        10     .86720251  
                                        11   .844864765  
                                        12   .903542662  
   
   
  COVAR_POP      
  功能描述:返回一对表达式的总体协方差。  
  SAMPLE:下例CUM_COVP返回定价和最小产品价格的累积总体协方差  
   
  SELECT   product_id,   supplier_id,  
                  COVAR_POP(list_price,   min_price)    
                      OVER   (ORDER   BY   product_id,   supplier_id)   AS   CUM_COVP,  
                  COVAR_SAMP(list_price,   min_price)  
                      OVER   (ORDER   BY   product_id,   supplier_id)   AS   CUM_COVS    
      FROM   product_information   p  
  WHERE   category_id   =   29  
  ORDER   BY   product_id,   supplier_id;  
   
  PRODUCT_ID   SUPPLIER_ID       CUM_COVP       CUM_COVS  
  ----------   -----------   ----------   ----------  
              1774             103088                     0  
              1775             103087         1473.25           2946.5  
              1794             103096   1702.77778   2554.16667  
              1825             103093         1926.25   2568.33333  
              2004             103086           1591.4         1989.25  
              2005             103086           1512.5               1815  
              2416             103088   1475.97959   1721.97619  
  .  
  .  
   
   
  COVAR_SAMP      
  功能描述:返回一对表达式的样本协方差  
  SAMPLE:下例CUM_COVS返回定价和最小产品价格的累积样本协方差  
   
  SELECT   product_id,   supplier_id,  
                  COVAR_POP(list_price,   min_price)    
                      OVER   (ORDER   BY   product_id,   supplier_id)   AS   CUM_COVP,  
                  COVAR_SAMP(list_price,   min_price)  
                      OVER   (ORDER   BY   product_id,   supplier_id)   AS   CUM_COVS    
      FROM   product_information   p  
  WHERE   category_id   =   29  
  ORDER   BY   product_id,   supplier_id;  
   
  PRODUCT_ID   SUPPLIER_ID       CUM_COVP       CUM_COVS  
  ----------   -----------   ----------   ----------  
              1774             103088                     0  
              1775             103087         1473.25           2946.5  
              1794             103096   1702.77778   2554.16667  
              1825             103093         1926.25   2568.33333  
              2004             103086           1591.4         1989.25  
              2005             103086           1512.5               1815  
              2416             103088   1475.97959   1721.97619  
  .  
  .  
  Top

25 楼waterfirer(水清)回复于 2005-08-01 16:23:38 得分 0

COUNT    
  功能描述:对一组内发生的事情进行累积计数,如果指定*或一些非空常数,count将对所有行计数,如果指定一个表达式,count返回表达式非空赋值的计数,当有相同值出现时,这些相等的值都会被纳入被计算的值;可以使用DISTINCT来记录去掉一组中完全相同的数据后出现的行数。  
  SAMPLE:下面例子中计算每个员工在按薪水排序中当前行附近薪水在[n-50,n+150]之间的行数,n表示当前行的薪水  
  例如,Philtanker的薪水2200,排在他之前的行中薪水大于等于2200-50的有1行,排在他之后的行中薪水小于等于2200+150的行没有,所以count计数值cnt3为2(包括自己当前行);cnt2值相当于小于等于当前行的SALARY值的所有行数  
   
  SELECT   last_name,   salary,   COUNT(*)   OVER   ()   AS   cnt1,  
                COUNT(*)   OVER   (ORDER   BY   salary)   AS   cnt2,  
                COUNT(*)   OVER   (ORDER   BY   salary   RANGE   BETWEEN   50   PRECEDING  
                AND   150   FOLLOWING)   AS   cnt3   FROM   employees;  
   
  LAST_NAME                                           SALARY               CNT1               CNT2               CNT3  
  -------------------------   ----------   ----------   ----------   ----------  
  Olson                                                       2100                 107                     1                     3  
  Markle                                                     2200                 107                     3                     2  
  Philtanker                                             2200                 107                     3                     2  
  Landry                                                     2400                 107                     5                     8  
  Gee                                                           2400                 107                     5                     8  
  Colmenares                                             2500                 107                   11                   10  
  Patel                                                       2500                 107                   11                   10  
  .  
  .  
   
   
  CUME_DIST    
  功能描述:计算一行在组中的相对位置,CUME_DIST总是返回大于0、小于或等于1的数,该数表示该行在N行中的位置。例如,在一个3行的组中,返回的累计分布值为1/3、2/3、3/3  
  SAMPLE:下例中计算每个工种的员工按薪水排序依次累积出现的分布百分比  
   
  SELECT   job_id,   last_name,   salary,   CUME_DIST()    
                OVER   (PARTITION   BY   job_id   ORDER   BY   salary)   AS   cume_dist  
      FROM   employees     WHERE   job_id   LIKE   'PU%';  
   
  JOB_ID           LAST_NAME                                           SALARY     CUME_DIST  
  ----------   -------------------------   ----------   ----------  
  PU_CLERK       Colmenares                                             2500                   .2  
  PU_CLERK       Himuro                                                     2600                   .4  
  PU_CLERK       Tobias                                                     2800                   .6  
  PU_CLERK       Baida                                                       2900                   .8  
  PU_CLERK       Khoo                                                         3100                     1  
  PU_MAN           Raphaely                                               11000                     1  
   
   
  DENSE_RANK    
  功能描述:根据ORDER   BY子句中表达式的值,从查询返回的每一行,计算它们与其它行的相对位置。组内的数据按ORDER   BY子句排序,然后给每一行赋一个号,从而形成一个序列,该序列从1开始,往后累加。每次ORDER   BY表达式的值发生变化时,该序列也随之增加。有同样值的行得到同样的数字序号(认为null时相等的)。密集的序列返回的时没有间隔的数  
  SAMPLE:下例中计算每个员工按部门分区再按薪水排序,依次出现的序列号(注意与RANK函数的区别)  
   
  SELECT   d.department_id   ,   e.last_name,   e.salary,   DENSE_RANK()    
                  OVER   (PARTITION   BY   e.department_id   ORDER   BY   e.salary)   as   drank  
      FROM   employees   e,   departments   d  
  WHERE   e.department_id   =   d.department_id  
        AND   d.department_id   IN   ('60',   '90');    
   
  DEPARTMENT_ID   LAST_NAME                                           SALARY             DRANK  
  -------------   -------------------------   ----------   ----------  
                        60   Lorentz                                                   4200                     1  
                        60   Austin                                                     4800                     2  
                        60   Pataballa                                               4800                     2  
                        60   Ernst                                                       6000                     3  
                        60   Hunold                                                     9000                     4  
                        90   Kochhar                                                 17000                     1  
                        90   De   Haan                                                 17000                     1  
                        90   King                                                       24000                     2  
   
   
  FIRST    
  功能描述:从DENSE_RANK返回的集合中取出排在最前面的一个值的行(可能多行,因为值可能相等),因此完整的语法需要在开始处加上一个集合函数以从中取出记录  
  SAMPLE:下面例子中DENSE_RANK按部门分区,再按佣金commission_pct排序,FIRST取出佣金最低的对应的所有行,然后前面的MAX函数从这个集合中取出薪水最低的值;LAST取出佣金最高的对应的所有行,然后前面的MIN函数从这个集合中取出薪水最高的值  
  SELECT   last_name,   department_id,   salary,  
                    MIN(salary)   KEEP   (DENSE_RANK   FIRST   ORDER   BY   commission_pct)  
                    OVER   (PARTITION   BY   department_id)   "Worst",  
                    MAX(salary)   KEEP   (DENSE_RANK   LAST   ORDER   BY   commission_pct)  
                    OVER   (PARTITION   BY   department_id)   "Best"  
      FROM   employees    
  WHERE   department_id   in   (20,80)    
  ORDER   BY   department_id,   salary;  
   
  LAST_NAME                                   DEPARTMENT_ID           SALARY             Worst               Best  
  -------------------------   -------------   ----------   ----------   ----------  
  Fay                                                                     20               6000               6000             13000  
  Hartstein                                                         20             13000               6000             13000  
  Kumar                                                                 80               6100               6100             14000  
  Banda                                                                 80               6200               6100             14000  
  Johnson                                                             80               6200               6100             14000  
  Ande                                                                   80               6400               6100             14000  
  Lee                                                                     80               6800               6100             14000  
  Tuvault                                                             80               7000               6100             14000  
  Sewall                                                               80               7000               6100             14000  
  Marvins                                                             80               7200               6100             14000  
  Bates                                                                 80               7300               6100             14000  
  .  
  .  
  .  
   
   
  FIRST_VALUE      
  功能描述:返回组中数据窗口的第一个值。  
  SAMPLE:下面例子计算按部门分区按薪水排序的数据窗口的第一个值对应的名字,如果薪水的第一个值有多个,则从多个对应的名字中取缺省排序的第一个名字  
   
  SELECT   department_id,   last_name,   salary,   FIRST_VALUE(last_name)  
      OVER   (PARTITION   BY   department_id   ORDER   BY   salary   ASC   )   AS   lowest_sal  
      FROM   employees    
  WHERE   department_id   in(20,30);  
   
  DEPARTMENT_ID   LAST_NAME                                           SALARY   LOWEST_SAL  
  -------------   -------------------------   ----------   --------------  
                        20   Fay                                                           6000   Fay  
                        20   Hartstein                                             13000   Fay  
                        30   Colmenares                                             2500   Colmenares  
                        30   Himuro                                                     2600   Colmenares  
                        30   Tobias                                                     2800   Colmenares  
                        30   Baida                                                       2900   Colmenares  
                        30   Khoo                                                         3100   Colmenares  
                        30   Raphaely                                               11000   Colmenares  
  Top

26 楼zzwind5(★★★★★)回复于 2005-08-01 17:56:31 得分 12

duanzilin   (寻)  
  经常看到你的回帖,感觉你比较善用ORACLE分析函数,有机会认识一下,  
  QQ77443484  
  最近也看到一个定义的SQL函数,比较方便能和GROUP   BY   连用  
  分享一下  
   
  Oracle9i   以后的活,创建函数  
   
  create   or   replace   type   ListImpl   as   object  
  (  
  listv   VARCHAR2(4000),  
   
  static   function   ODCIAggregateInitialize(sctx   IN   OUT   ListImpl)  
  return   number,  
   
  member   function   ODCIAggregateIterate(self   IN   OUT   ListImpl,  
  value   IN   varchar)   return   number,  
   
  member   function   ODCIAggregateTerminate(self   IN   OUT   ListImpl,  
  returnValue   OUT   varchar,   flags   IN   number)  
  return   number,  
   
  member   function   ODCIAggregateMerge(self   IN   OUT   ListImpl,  
  ctx2   IN   ListImpl)   return   number  
  );  
  /  
  show   error  
   
  create   or   replace   type   body   ListImpl   is  
  static   function   ODCIAggregateInitialize(sctx   IN   OUT   ListImpl)  
  return   number   is  
  begin  
  sctx   :=   ListImpl(null);  
  return   ODCIConst.Success;  
  end;  
   
  member   function   ODCIAggregateIterate(self   IN   OUT   ListImpl,  
  value   IN   varchar)   return   number   is  
  begin  
  if   (self.listv   is   null)   then  
  self.listv   :=   rtrim(value);  
  elsif   (value   is   not   null)   then  
  self.listv   :=   self.listv||','||rtrim(value);  
  end   if;  
  return   ODCIConst.Success;  
  end;  
   
  member   function   ODCIAggregateTerminate(self   IN   OUT   ListImpl,  
  returnValue   OUT   varchar,   flags   IN   number)   return   number   is  
  begin  
  returnValue   :=   self.listv;  
  return   ODCIConst.Success;  
  end;  
   
  member   function   ODCIAggregateMerge(self   IN   OUT   ListImpl,  
  ctx2   IN   ListImpl)   return   number   is  
  begin  
  if   (self.listv   is   null)   then  
  self.listv   :=   ctx2.listv;  
  elsif   (ctx2.listv   is   not   null)   then  
  self.listv   :=   self.listv||','||ctx2.listv;  
  end   if;  
  return   ODCIConst.Success;  
  end;  
  end;  
  /  
  show   error  
   
  create   or   replace   function   LIST(input   varchar)   return   varchar  
  parallel_enable   aggregate   using   ListImpl;  
  /  
  show   error  
   
   
  然后,用list函数  
   
  select   NAME,list(CONTENT)   CONTENT   from   "表A"   group   by   NAME;Top

27 楼waterfirer(水清)回复于 2005-08-01 18:05:10 得分 0

to   bzszp(SongZip):Oracle   9i   分析函数参考手册     太长了,多次回复又不行,给个地址吧。  
  全靠斑竹整理了。  
  http://www.cnoug.org/viewthread.php?tid=38387Top

28 楼duanzilin(寻)回复于 2005-08-01 20:26:31 得分 0

我觉得不用全部列出来,只要列出比较常用的就可以了,像协方差函数COVAR_之类的太专业了,很难理解,一般也不会用到,常用的一般是下面这些:  
  1.AVG  
  2.COUNT  
  3.DENSE_RANK、RANK、PERCENT_RANK    
  4.FIRST_VALUE、LAST_VALUE  
  5.LAG、LEAD  
  6.MAX、MIN  
  7.NTILE    
  8.ROW_NUMBER    
  9.SUM  
  我接着waterfirer(水清)的补充下其他的几个函数用法:  
   
  LAST_VALUE  
  功能描述:返回组中数据窗口的第一个值。  
  用法和first_value一样,不用多说了,不过LAST_VALUE函数本身好像有问题,结果和预期不一样,至少9i是这样,大家可以去试验一下  
   
  LAG、LEAD  
  功能描述:可以访问结果集中的其它行而不用进行自连接。它允许去处理游标,就好像游标是一个数组一样。在给定组中可参考当前行之前的行,这样就可以从组中与当前行一起选择以前的行。Offset是一个正整数,其默认值为1,若索引超出窗口的范围,就返回默认值(默认返回的是组中第一行),其相反的函数是LEAD,LEAD是返回当前行以后的行  
  SAMPLE:下面的例子中列prev_sal返回按hire_date排序的前1行的salary值  
   
  SELECT   last_name,   hire_date,   salary,  
  LAG(salary,   1,   0)   OVER   (ORDER   BY   hire_date)   AS   prev_sal  
  FROM   employees  
  WHERE   job_id   =   'PU_CLERK';  
   
  LAST_NAME   HIRE_DATE   SALARY   PREV_SAL  
  -------------------------   ----------   ----------   ----------  
  Khoo   18-5月   -95   3100   0  
  Tobias   24-7月   -97   2800   3100  
  Baida   24-12月-97   2900   2800  
  Himuro   15-11月-98   2600   2900  
  Colmenares   10-8月   -99   2500   2600  
   
  MAX   、MIN  
  功能描述:在一个组中的数据窗口中查找表达式的最大值和最小值。  
  SAMPLE:下面例子中dept_max返回当前行所在部门的最大薪水值  
   
  SELECT   department_id,   last_name,   salary,    
  MAX(salary)   OVER   (PARTITION   BY   department_id)   AS   dept_max  
  FROM   employees   WHERE   department_id   in   (10,20,30);  
   
  DEPARTMENT_ID   LAST_NAME   SALARY   DEPT_MAX  
  -------------   -------------------------   ----------   ----------  
  10   Whalen   4400   4400  
  20   Hartstein   13000   13000  
  20   Fay   6000   13000  
  30   Raphaely   11000   11000  
  30   Khoo   3100   11000  
  30   Baida   2900   11000  
  30   Tobias   2800   11000  
  30   Himuro   2600   11000  
  30   Colmenares   2500   11000  
   
  NTILE    
  功能描述:将一个组分为"表达式"的散列表示,例如,如果表达式=4,则给组中的每一行分配一个数(从1到4),如果组中有20行,则给前5行分配1,给下5行分配2等等。如果组的基数不能由表达式值平均分开,则对这些行进行分配时,组中就没有任何percentile的行数比其它percentile的行数超过一行,最低的percentile是那些拥有额外行的percentile。例如,若表达式=4,行数=21,则percentile=1的有5行,percentile=2的有5行等等。  
  SAMPLE:下例中把6行数据分为4份  
   
  SELECT   last_name,   salary,    
  NTILE(4)   OVER   (ORDER   BY   salary   DESC)   AS   quartile   FROM   employees  
  WHERE   department_id   =   100;  
   
  LAST_NAME   SALARY   QUARTILE  
  -------------------------   ----------   ----------  
  Greenberg   12000   1  
  Faviet   9000   1  
  Chen   8200   2  
  Urman   7800   2  
  Sciarra   7700   3  
  Popp   6900   4  
   
  RANK    
  功能描述:根据ORDER   BY子句中表达式的值,从查询返回的每一行,计算它们与其它行的相对位置。组内的数据按ORDER   BY子句排序,然后给每一行赋一个号,从而形成一个序列,该序列从1开始,往后累加。每次ORDER   BY表达式的值发生变化时,该序列也随之增加。有同样值的行得到同样的数字序号(认为null时相等的)。然而,如果两行的确得到同样的排序,则序数将随后跳跃。若两行序数为1,则没有序数2,序列将给组中的下一行分配值3,DENSE_RANK则没有任何跳跃。  
  SAMPLE:下例中计算每个员工按部门分区再按薪水排序,依次出现的序列号(注意与DENSE_RANK函数的区别)  
   
  SELECT   d.department_id   ,   e.last_name,   e.salary,   RANK()    
  OVER   (PARTITION   BY   e.department_id   ORDER   BY   e.salary)   as   drank  
  FROM   employees   e,   departments   d  
  WHERE   e.department_id   =   d.department_id  
  AND   d.department_id   IN   ('60',   '90');  
   
  DEPARTMENT_ID   LAST_NAME   SALARY   DRANK  
  -------------   -------------------------   ----------   ----------  
  60   Lorentz   4200   1  
  60   Austin   4800   2  
  60   Pataballa   4800   2  
  60   Ernst   6000   4  
  60   Hunold   9000   5  
  90   Kochhar   17000   1  
  90   De   Haan   17000   1  
  90   King   24000   3  
   
  PERCENT_RANK    
  功能描述:和CUME_DIST(累积分配)函数类似,对于一个组中给定的行来说,在计算那行的序号时,先减1,然后除以n-1(n为组中所有的行数)。该函数总是返回0~1(包括1)之间的数。  
  SAMPLE:下例中如果Khoo的salary为2900,则pr值为0.6,因为RANK函数对于等值的返回序列值是一样的  
   
  SELECT   department_id,   last_name,   salary,    
  PERCENT_RANK()    
  OVER   (PARTITION   BY   department_id   ORDER   BY   salary)   AS   pr  
  FROM   employees  
  WHERE   department_id   <   50  
  ORDER   BY   department_id,salary;  
   
  DEPARTMENT_ID   LAST_NAME   SALARY   PR  
  -------------   -------------------------   ----------   ----------  
  10   Whalen   4400   0  
  20   Fay   6000   0  
  20   Hartstein   13000   1  
  30   Colmenares   2500   0  
  30   Himuro   2600   0.2  
  30   Tobias   2800   0.4  
  30   Baida   2900   0.6  
  30   Khoo   3100   0.8  
  30   Raphaely   11000   1  
  40   Mavris   6500   0  
   
  ROW_NUMBER    
  功能描述:返回有序组中一行的偏移量,从而可用于按特定标准排序的行号。  
  SAMPLE:下例返回每个员工再在每个部门中按员工号排序后的顺序号  
   
  SELECT   department_id,   last_name,   employee_id,   ROW_NUMBER()  
  OVER   (PARTITION   BY   department_id   ORDER   BY   employee_id)   AS   emp_id  
  FROM   employees  
  WHERE   department_id   <   50;  
   
  DEPARTMENT_ID   LAST_NAME   EMPLOYEE_ID   EMP_ID  
  -------------   -------------------------   -----------   ----------  
  10   Whalen   200   1  
  20   Hartstein   201   1  
  20   Fay   202   2  
  30   Raphaely   114   1  
  30   Khoo   115   2  
  30   Baida   116   3  
  30   Tobias   117   4  
  30   Himuro   118   5  
  30   Colmenares   119   6  
  40   Mavris   203   1  
   
  SUM    
  功能描述:该函数计算组中表达式的累积和。  
  SAMPLE:下例计算同一经理下员工的薪水累积值  
   
  SELECT   manager_id,   last_name,   salary,  
  SUM   (salary)   OVER   (PARTITION   BY   manager_id   ORDER   BY   salary  
  RANGE   UNBOUNDED   PRECEDING)   l_csum  
  FROM   employees  
  WHERE   manager_id   in   (101,103,108);  
   
  MANAGER_ID   LAST_NAME   SALARY   L_CSUM  
  ----------   -------------------------   ----------   ----------  
  101   Whalen   4400   4400  
  101   Mavris   6500   10900  
  101   Baer   10000   20900  
  101   Greenberg   12000   44900  
  101   Higgins   12000   44900  
  103   Lorentz   4200   4200  
  103   Austin   4800   13800  
  103   Pataballa   4800   13800  
  103   Ernst   6000   19800  
  108   Popp   6900   6900  
  108   Sciarra   7700   14600  
  108   Urman   7800   22400  
  108   Chen   8200   30600  
  108   Faviet   9000   39600  
  Top

29 楼duanzilin(寻)回复于 2005-08-01 21:01:39 得分 0

上面的例子可能会比较难看懂,关键还是自己动手做了  
   
  再贴一下分析函数的框架结构吧  
  analytic_function::=  
  analytic_function   ([   arguments   ])   OVER   (analytic_clause)  
   
  analytic_clause::=  
  [   query_partition_clause   ]  
  [   order_by_clause   [   windowing_clause   ]   ]  
   
  query_partition_clause::=  
  PARTITION   BY   value_expr   [,   value_expr]...  
   
  order_by_clause::=  
  ORDER   [SIBLINGS]   BY    
  {   expr   |   position   |   c_alias   }   [   ASC   |   DESC   ]   [   NULLS   FIRST   |   NULLS   LAST   ]    
  [,   {   expr   |   position   |   c_alias   }   [   ASC   |   DESC   ]   [   NULLS   FIRST   |   NULLS   LAST   ]]...  
   
  windowing_clause::=  
  {   ROWS   |   RANGE   }  
  {   BETWEEN  
      {   UNBOUNDED   PRECEDING  
      |   CURRENT   ROW  
      |   value_expr   {   PRECEDING   |   FOLLOWING   }  
      }    
      AND  
      {   UNBOUNDED   FOLLOWING  
      |   CURRENT   ROW  
      |   value_expr   {   PRECEDING   |   FOLLOWING   }  
      }  
  |   {   UNBOUNDED   PRECEDING  
      |   CURRENT   ROW  
      |   value_expr   PRECEDING  
      }  
  }   
    
 

你可能感兴趣的:(oracle,sql,function,manager,null,Path)