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

利用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内置SQL函数-分类整理大全.doc

    ### Oracle内置SQL函数-分类整理大全 #### F.1 字符函数——返回字符值 **1.1.1 CHR** - **语法**: `CHR(x)` - **功能**: 返回在数据库字符集中与`x`拥有等价数值的字符。`CHR`和`ASCII`是一对反函数,即经过`CHR...

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

    本文将深入探讨Oracle内置的SQL函数,这些函数极大地丰富了数据查询、处理和分析的能力。 一、数学函数 Oracle提供了丰富的数学函数,如ROUND、TRUNC、MOD、SQRT等。ROUND用于四舍五入,例如ROUND(123.456, 2)将...

    Oracle内置SQL函数

    Oracle数据库系统是世界上最广泛使用的数据库管理...通过熟练掌握这些Oracle内置的SQL函数,开发者可以更高效地处理和管理数据库中的数据。在sqlplus环境中,可以编写脚本文件并执行,实现自动化操作,提高工作效率。

    oracle分析函数指南

    Oracle分析函数是数据库查询中的重要工具,主要用于处理复杂的聚合和排序操作,特别是在OLAP(在线分析处理)系统中,它们能够高效地进行数据汇总和分析。分析函数与聚合函数(如SUM, AVG, COUNT等)有所不同,聚合...

    oracle最全函数大全

    ### Oracle 最全函数大全 #### 一、总体介绍 ...分析函数特别适合处理复杂的数据分析任务,而聚合函数则是进行统计分析的基础。掌握这些函数能够帮助开发人员更加高效地管理和操作数据库中的数据。

    ORACLE的内置函数

    Oracle数据库系统是世界上最广泛使用的数据库管理系统之一,其强大的功能和高效的数据处理能力离不开丰富的内置函数。这些函数在SQL查询、数据处理、计算以及各种复杂的业务逻辑中起着至关重要的作用。下面将详细...

    oracle函数大全(分类成9个word文档)

    5. "oracle函数介绍(6) 著名函数之分析函数.doc"和"oracle函数介绍(7) 非著名函数之分析函数.doc"会进一步详细讨论这些高级函数,可能包括窗口函数的用法和实例。 6. "oracle函数介绍(8) 综述.doc"应该是对前面所有...

    oracle常用函数chm版

    6. **分析函数**:分析函数在处理大量数据时非常有用,如`RANK`、`DENSE_RANK`和`ROW_NUMBER`用于生成行序号,`LEAD`和`LAG`可以访问当前行的前一行或后一行数据,`FIRST_VALUE`和`LAST_VALUE`则用于获取分组内第一...

    oracle函数分类.rar

    **聚组函数**如`COUNT`、`SUM`、`AVG`、`MAX`和`MIN`用于对一组数据进行统计汇总。它们常在SQL查询的`GROUP BY`语句中使用,帮助我们快速获取每个分组的计数、总和、平均值、最大值和最小值。 **分析函数**如`RANK`...

    Oracle常见函数集锦

    Oracle数据库是世界上最广泛使用的数据库系统之一,其强大的功能和丰富的内置函数使得数据处理变得高效而灵活。本篇文章将深入探讨“Oracle常见函数集锦”,主要关注在SQL查询中常用的Oracle函数,这些函数对于数据...

    oracle函数大全(chm格式分类显示)

    Oracle数据库系统是世界上最广泛使用的数据库管理系统之一,它提供了丰富的内置函数来支持各种数据处理和查询需求。本资源“oracle函数大全(chm格式分类显示)”详细介绍了Oracle中的主要函数类别,帮助用户更好地...

    ORACLE函数大全汇总详解(20100915补充修订版)

    本文档将详细介绍一系列Oracle函数及其应用场景,帮助用户更好地理解和掌握如何利用这些函数进行高效的数据处理。 #### 二、字符串函数 1. **ASCII**: 返回字符的ASCII码。 - 示例:`ASCII('A')` 返回65。 2. **...

    ORACLE函数大全

    7. **分组和分析函数**: - `GROUP BY`:根据一个或多个列对结果集进行分组。 - `ROLLUP`:创建分组的多级汇总。 - `CUBE`:生成所有可能的子集组合。 8. **连接查询相关函数**: - `JOIN`:将两个或更多表的...

    oracle函数大全

    在数据分析中,`COUNT()`、`SUM()`、`AVG()`、`MAX()`和`MIN()`等聚合函数用于对一组值进行统计汇总。它们在报表和数据分析中扮演着重要角色。 七、分组和排序函数 `GROUP BY`语句配合聚合函数用于对数据进行分组,...

    oracle书籍

    9. 分组和分析函数:如GROUP BY、ROLLUP、CUBE和RANK等,用于数据的分组汇总和复杂分析,是大数据处理和OLAP操作的关键。 10. 连接函数:如CONNECT_BY_ROOT和START_WITH,用于构建复杂的树状查询,处理层次结构数据...

    Oracle函数手册.zip

    6. **分组和分析函数**: - `GROUP BY`: 用于对数据进行分组。 - `ROLLUP()`: 生成多级汇总。 - `CUBE()`: 生成所有可能的子集组合。 - `RANK()`, `DENSE_RANK()`, `ROW_NUMBER()`: 用于排序和生成行号。 7. **...

    Oracle常用函数使用方法及常用查询

    在Oracle数据库中,SQL函数被广泛应用于各种数据处理需求,例如数据筛选、计算汇总、格式化等。本文将详细介绍Oracle中的常见函数及其应用场景,帮助用户更好地理解和掌握这些实用工具。 #### 一、基本统计函数 1....

Global site tag (gtag.js) - Google Analytics