`
hypgr
  • 浏览: 273079 次
社区版块
存档分类
最新评论

利用Oracle内置分析函数进行高效统计汇总(1)

阅读更多

分析函数是Oracle从8.1.6开始引入的一个新的概念,为我们分析数据提供了一种简单高效的处理方式。在分析函数出现以前,我们必须使用自联查询,子查询或者内联视图,甚至复杂的存储过程实现的语句,现在只要一条简单的SQL语句就可以实现了,而且在执行效率方面也有相当大的提高。下面我将针对分析函数做一些具体的说明。

分析函数的一般格式是函数名(参数列表) over ([partition by 字段名或表达式] [order by 字段名或表达式]),其中over()部分称为开窗函数,它是可以选填的。

    开窗函数指定了分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化,举例如下:

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)

分析函数用于计算基于组的某种聚合值,它和聚合函数的不同之处是对于每个组返回多行,而聚合函数对于每个组只返回一行。

许多分析函数同时也是聚合函数,比如sum()函数,这样使用就是聚合函数。

SQL> select department_id,sum(salary) sum_salary from employees group by department_id;

而这样使用就是分析函数。

SQL> select distinct department_id,sum(salary) over(partition by department_id) sum_salary from employees ;

它们得出的结果是相同的,都是:

DEPARTMENT_ID SUM_SALARY

------------- ----------

           10       4400

           20      19000

           30      24900

           40       6500

           50     156400

           60      28800

           70      10000

           80     304500

           90      58000

          100      51600

          110      20300

                    7000

 

已选择12行。

请注意,这里我们用到了distinct 关键字,如果不用distinct,第2个查询将返回107行数据,即employees表的每行记录都将返回一行sum_salary,因为不用distinct的含义是:针对每个雇员计算他/她所在的部门的薪金总数。

在这个例子中,聚合函数是更好的选择,但在另外一些情形下,我们更应该使用分析函数。

下面通过几个实例来介绍部分分析函数的用途。

问题1:求出每个省工业企业利润总额最多的前10名。

利用我们传统的聚合函数max可以方便地取出利润总额最多的一家,但是取出多家就无能为力了,同样,如果不分组我们可以通过排序取出任何一个省利润总额最多的前10名,但无法实现对多个省的分组。而采用rank聚合函数,可以方便地实现我们的要求。

完整的语句如下:

select * from

(select  substr(z01_04,1,2) 地区码,

DENSE_RANK() OVER (PARTITION BY substr(z01_04,1,2) order by b04_50 desc) 名次, b04_50 "利润总额"

from cj604,cj601 where b04_50>0 and cj601.uuid=cj604.uuid  ) where 名次<=10;

我们在开窗函数中使用地区码作为分组标志,并按照利润总额倒序排列。

结果如下(数据为模拟数据,以下同)

地区       名次   利润总额

---- ---------- ----------

31            1     963799

31            2     229643

...

31            9     135917

31           10     125245

32            1     349940

32            2     300587

...

注意:RANK()函数有3组,分别是rank, dense_rank, row_number,它们的区别是:

rank如果出现两个相同的数据,那么后面的数据就会直接跳过这个排名,比如:当第2名和第3名的利润相同时,rank的结果是1,2,2,4;而dense_rank则不会跳过这个排名,结果是1,2,2,3;而row_number哪怕是两个数据完全相同,排名也会不一样,结果是1,2,3,4

 

问题2:求出按登记注册类型分组的职工人数和销售额占总体的比重

分析函数ratio_to_report专门用来解决个体占总体的比重一类的问题。

语句

select d.*,round((ratio_to_report(职工人数) over())*100,1) as 人数百分比,

round((ratio_to_report(销售额) over())*100,1) as 销售额百分比

from

(select c.code 代码 , substr(b.reg_type,1,10) 登记注册类型, 职工人数, 销售额 from

(select substr(z01_08,1,1)||'00' code, sum(z01_171_01) 职工人数,sum(b03_01) 销售额

from cj603 c,cj601

j where c.uuid=j.uuid group by  substr(z01_08,1,1)

)c, djzclx b where c.code=b.reg_code

)d;

可以得出下面的结果:

代码 登记注册类型           职工人数     销售额 人数百分比 销售额百分比

---- -------------------- ---------- ---------- ---------- ------------

100  内资企业                8510509 3002627283         63      56.2

200  港、澳、台商投资企业    2066175  746728306       15.3        14

300  外商投资企业            2936984 1597046896       21.7      29.9

 

其中内层的子查询语句

select substr(z01_08,1,1)||'00' code, sum(z01_171_01) 职工人数,sum(b03_01) 销售额

from cj603 c,cj601 j where c.uuid=j.uuid group by  substr(z01_08,1,1)

获得如下的结果

CODE   职工人数     销售额

---- ---------- ----------

100     8510509 3002627283

200     2066175  746728306

300     2936984 1597046896

外层查询中ratio_to_report函数自动对结果集中的职工人数和销售额计算比重。

 

问题3 求按行业中类划分的大中小型企业个数

case语句不是分析函数,但它在统计汇总中的作用非常重要,可以用来设定复杂的分组条件。

以下是统计上工业大中小型企业划分标准。

指标名称

计量单位

大型

中型

小型

从业人员数

2000及以上且

300-2000以下

300以下或

销售收入

万元

30000及以上且

3000-30000以下

3000以下或

资产合计

万元

40000及以上

4000-40000以下

4000以下

 

请注意下面这个说明:大型和中型企业须同时满足所列各项条件的下限指标,否则下划一档。

比如某企业虽然从业人员数和销售收入符合大型企业的要求,但资产合计30000万元,不满足大型企业的要求,只能划归中型企业。实际上,中型企业单位数=企业单位总数-大型企业单位数-小型企业单位数。

因此,用b04_71<2000 and b04_71>=300 and b04_29>=30000 and b04_29<300000 and b04_16>=40000 and b04_16<400000的写法来表述中型标准是错误的。

正确写法应该是:

not(b04_71>=2000 and b04_29>=300000 and b04_16>=400000) and not(b04_71<300 or b04_29<30000 or b04_16<40000),当然前提是这3个字段没有空值null。

完整的SQL语句如下:

select code 代码 , substr(INDUSTRY_NAME,1,10) 行业名称, c.*  from

(

select substr(z01_064,1,2) as code ,count(*) as TOL,

count(case when b04_71>=2000 and b04_29>=300000 and b04_16>=400000 then 1 else null end) as big,

count(case when not(b04_71>=2000 and b04_29>=300000 and b04_16>=400000)

and not(b04_71<300 or b04_29<30000 or b04_16<40000) then 1 else null end) as mid,

count(case when b04_71<300 or b04_29<30000 or b04_16<40000 then 1 else null end) as small

from cj604 a,cj601 b where a.uuid=b.uuid group by substr(z01_064,1,2)

)c, industry b where c.code=b.INDUSTRY_CODE

输出结果如下:

代码 行业名称           CODE        TOL        BIG        MID      SMALL                                                                                               

---- ------------------ ---- ---------- ---------- ---------- ----------                                                                                                

06   煤炭开采和洗选业   06            9          1          2          6                                                                                               

07   石油和天然气开采业 07            3          1          0          2                                                                                               

08   黑色金属矿采选业   08           13          1          3          8                                                                                                

13   农副食品加工业     13         1342          2         48       1269                                                                                               

14   食品制造业         14          784          3         66        691                                                                                               

15   饮料制造业         15          385          0         31        331                                                                                                

...

 

问题4 求按地区划分的3种登记注册类型的营业利润率

decode函数不是分析函数,但它在统计汇总中的作用非常重要,它的格式是:

decode(字段名或表达式,比较值1,返回值1, [比较值2,返回值2,...] 默认返回值),它的作用是当字段或表达式的值等于比较值1时,就得出返回值1,当字段或表达式的值等于比较值2时,就得出返回值2,以此类推,如果都不符合,就返回默认返回值。其中从比较值2开始的参数对可以不提供。

语句

select substr(name,1,4) 地区名称, c.*

from(select 地区代码,

decode(注册类型码,'1',营业利润率,null) A1,

decode(注册类型码,'2',营业利润率,null) A2,

decode(注册类型码,'3',营业利润率,null) A3

from(

select

substr(z01_04,1,2) 地区代码,substr(z01_08,1,1) 注册类型码,

round(sum(b04_45)/sum(b04_29)*100,2) 营业利润率

from cj601 a,cj604 b where a.uuid=b.uuid

group by substr(z01_04,1,2),substr(z01_08,1,1)

)

)c,dq

where 地区代码=dq.code;

得出如下结果。

地区名称 地区     A1     A2     A3

-------- ---- ------ ------ ------

上海     31     6.74

上海     31            5.30

上海     31                   6.37

江苏     32     3.94

江苏     32            4.85

江苏     32                   4.32

浙江     33     4.55

浙江     33            5.25

浙江     33                   5.76

因为decode函数只针对一行内的数据进行处理,这样的结果并不符合要求,我们需要在第二层查询语句的外面再加一层按地区代码的分组汇总,完整写法如下:

select substr(name,1,4) 地区名称, c.*

from(

select 地区代码,SUM(A1) A1,SUM(A2) A2,SUM(A3) A3 from(

select 地区代码,

decode(注册类型码,'1',营业利润率,null) A1,

decode(注册类型码,'2',营业利润率,null) A2,

decode(注册类型码,'3',营业利润率,null) A3

from(

select

substr(z01_04,1,2) 地区代码,substr(z01_08,1,1) 注册类型码,

round(sum(b04_45)/sum(b04_29)*100,2) 营业利润率

from cj601 a,cj604 b where a.uuid=b.uuid

group by substr(z01_04,1,2),substr(z01_08,1,1)

)

)group by 地区代码 )c,dq

where 地区代码=dq.code;

这样就得到了正确的结果:

地区名称 地区     A1     A2     A3

------- ---- ------ ------ ------

上海     31     6.74   5.30   6.37

江苏     32     3.94   4.85   4.32

浙江     33     4.55   5.25   5.76

 

同样的问题我们也可以通过lead分析函数来完成。

select substr(name,1,4) 地区名称, 地区代码, A1,A2,A3

from(

select * from(

select 地区代码,

lead(营业利润率,    0) over(partition by 地区代码 order by 注册类型码) A1,

lead(营业利润率,    1) over(partition by 地区代码 order by 注册类型码) A2,

lead(营业利润率,    2) over(partition by 地区代码 order by 注册类型码) A3,

row_number( ) over(partition by 地区代码 order by 注册类型码) rn

from(

select

substr(z01_04,1,2) 地区代码,substr(z01_08,1,1) 注册类型码,

round(sum(b04_45)/sum(b04_29)*100,2) 营业利润率

from cj601 a,cj604 b where a.uuid=b.uuid

group by substr(z01_04,1,2),substr(z01_08,1,1)

))where rn=1

)c,dq

where 地区代码=dq.code;

lead函数的第一个参数是我们关心的值,第2个参数是偏移量n,对本例就是下n种注册类型码。

之所以要限定rn=1,还是因为分析函数对每一行都返回分组值,而我们关心的是注册类型为1的那一行。

利用lag和lead函数,我们可以在同一行中显示前n行的数据,也可以显示后n行的数据。

如果本例改用lag函数实现,代码如下:

注意过滤条件rn=3以及lag函数第2个参数的变化,我们把第3行作为当前行,取出它前面的2行。

select substr(name,1,4) 地区名称, 地区代码, A1,A2,A3

from(

select * from(

select 地区代码,

lag(营业利润率,    2) over(partition by 地区代码 order by 注册类型码) A1,

lag(营业利润率,    1) over(partition by 地区代码 order by 注册类型码) A2,

lag(营业利润率,    0) over(partition by 地区代码 order by 注册类型码) A3,

row_number( ) over(partition by 地区代码 order by 注册类型码) rn

from(

select

substr(z01_04,1,2) 地区代码,substr(z01_08,1,1) 注册类型码,

round(sum(b04_45)/sum(b04_29)*100,2) 营业利润率

from cj601 a,cj604 b where a.uuid=b.uuid

group by substr(z01_04,1,2),substr(z01_08,1,1)

))where rn=3

)c,dq

where 地区代码=dq.code;

这种方法比前一种方法利用sum分组汇总的好处是对字符类型和其他非数值类型字段都可以采用。

分享到:
评论

相关推荐

    oracle的分析函数汇总

    oracle的分析函数汇总oracle的分析函数汇总

    oracle 内置函数大全

    oracle 内置函数大全

    oracle sql内置函数大全

    oracle sql内置函数大全

    Oracle SQL 内置函数大全

    Oracle SQL 内置函数大全 SQL中的单记录函数 给出整数,返回对应的字符 连接两个字符串 增加或减去月份 用于对查询到的结果进行排序输出

    Oracle 内置函数PPT

    Oracle 内置函数PPT,可以方便查询Oracle内置函数,为开发人员的好助手。

    Oracle内置函数大全

    Oracle内置函数大全,一般地,日期格式说明符是不太敏感的。然而,当为了显示而说明日期格式、对于文本数据中的说明符等情况下,它就变得比较务实、具体了

    Oracle 内置函数大全

    Oracle_SQL_内置函数大全 Oracle内置函数大全

    Oracle内置SQL函数-分类整理大全.doc

    Oracle内置SQL函数-分类...Oracle内置SQL函数-分类整理大全 1 F.1字符函数——返回字符值 1 F.2 字符函数——返回数字 3 F.3 数字函数 5 F.4 日期函数 7 F.5 转 换 函 数 8 F.6 分 组 函 数 11 F.7 其 他 函 数 12

    ORACLE 分析函数大全

    ORACLE 分析函数大全,包含很多关于ORACLE的分析函数,内置函数

    ORACLE常用分析函数说明

    ORACLE常用分析函数说明,oracle优化

    ORACLE分析函数大全

    文档详细介绍了oracle的分析函数,包括功能说明、sql示例等。分析函数功能强大,在报表或数据迁移的时候可能会使用到。分析函数用法看上去有点复杂,最好使用的时候,参考文档

    ORACLE内置函数大全

    oracle函数大全,介绍ORACLE一些 常用函数

    oracle内置函数

    Oracle内置SQL函数-分类整理大全Oracle内置SQL函数-分类整理大全

    ORACLE 常用分析函数

    主要包括分析函数(OVER);分析函数2(Rank, Dense_rank, row_number);分析函数3(Top/Bottom N、First/Last、NTile);窗口函数;报表函数;分析函数总结;26个分析函数;PLSQL开发笔记和小结;分析函数简述  ROW_NUMBER () ...

    oracle内置函数大全

    涵盖了oracle中所有的内置函数,可随搜随用,也供初学者学习使用。

    Oracle内置SQL函数-分类整理大全

    Oracle内置SQL函数-分类整理大全

    Oracle之分析函数.pdf

    分析函数是 Oracle 专门用于解决复杂报表统计需求的功能强大的函数,它可以在数据行分组然后计算基于组的某种统计值,并且每一组的每一行都可以返回一个统计。本文总结了若干分析函数的使用方式,供大家学习参考,有...

    ORACLE_分析函数大全

    ORACLE分析函数大全 包括大部分常用的分析函数

    oracle 内置sql函数

    oracle 内置sql函数 ,内置函数详细说明,

Global site tag (gtag.js) - Google Analytics