现在的位置: 首页 > 综合 > 正文

Oracle8i和Oracle9i中的分析函数

2012年08月25日 ⁄ 综合 ⁄ 共 6048字 ⁄ 字号 评论关闭
 
 
Oracle8iOracle9i中的分析函数(译)
 

内容

 

    概要介绍

    分析函数如何工作?

    语法

    计算动态总和

       Top-N查询

           1

           2  

    窗口

      范围窗口

      计算特定范围内的平均工资
          
行窗口
         
访问你当前行附近的行
       LAG
       LEAD
    确定一个集合中的头值、尾值

    跨表查询,还是重点查询

    结论

 

 

概论

 

    分析函数从Oracle 8.1.6开始出现,用于解决诸如“计算动态总和”、“计算集合中的百分比”、“查询Top-N”、“计算移动平均数”以及更多的问题。其中许多问题可以使用标准PL/SQL解决,但性能不好。分析函数对于SQL语言进行了扩展,不仅体现在易于对这些操作编码,还体现在与纯SQLPL/SQL在速度上的提高。ANSI SQL委员会正在对这些扩展进行审查以便将它们包含进SQL规范中来。 

 

分析函数如何工作?

 

    分析函数基于一个数据行的集合计算聚合值。它们有别于对每个集合返回多行的聚合函数。数据行的集合被称为窗口,可以通过分析从句来定义。每一行都可以定义一个“滑动”窗口。基于“当前行”的窗口决定了用于进行计算的数据行的范围。窗口大小可以基于物理上的数据行数,也可以基于一个逻辑间隔,比如说时间。分析函数在查询语句中仅order by从句之前。所有的连接和所有的wheregroup byhaving从句都位于分析函数之前。因此,分析函数旨在select列表或者order从句中出现。

 

语法     查询分割从句

    分析函数的语法非常简单直接

       分析函数(<参数><参数>,……)

       OVER(

              <查询分割从句>

              <Order by从句>

              <窗口从句>

      

 

    分析函数

 

      Oracle提供了许多分析函数,比如AVG, CORR, COVAR_POP, COVAR_SAMP, COUNT, CUME_DIST, DENSE_RANK, FIRST, FIRST_VALUE, LAG, LAST, LAST_VALUE, LEAD, MAX, MIN, NTILE, PERCENT_RANK, PERCENTILE_CONT, PERCENTILE_DISC, RANK, RATIO_TO_REPORT, STDDEV, STDDEV_POP, STDDEV_SAMP, SUM, VAR_POP, VAR_SAMP, VARIANCE,等等。

   

    参数

 

    分析函数可以有03个参数

 

 

 

    由于通过partition表达式将标准集合分为N个集合,Partition By从句逻辑上使得对于N个集合不再有单一的结果集。“partition”和“group”在这里意思一样。分析函数被独立应用于各个集合,对于每一个集合来说它们都是从头开始计算的。

 

      Order-By从句

 

      Order By从句用于指出数据如何存储在每一个集合中。这将对任何一个分析函数的输出产生重要影响。

 

    窗口从句

 

    窗口从句给我们一种定义滑动或固定数据窗口的方法,分析函数将在特定集合中的这个窗口范围内操作。这个从句可以用于使用分析函数计算特定集合中任意滑动或固定窗口中的数值

例:计算动态总和 

这个例子显示了某部门中按行计算的累积工资值,每行都包括前面所有行的工资在内。

 

 

set autotrace traceonly explain
break on deptno skip 1
column ename format A6
column deptno format 999
column sal format 99999
column seq format 999

SELECT ename "Ename", deptno "Deptno", sal "Sal",
  SUM(sal)
    OVER (ORDER BY deptno, ename) "Running Total",
  SUM(SAL)
    OVER (PARTITION BY deptno
          ORDER BY ename) "Dept Total",
  ROW_NUMBER()
    OVER (PARTITION BY deptno
          ORDER BY ENAME) "Seq"
FROM emp
ORDER BY deptno, ename
/

Ename  Deptno    Sal Running Total Dept Total  Seq
------ ------ ------ ------------- ---------- ----
CLARK      10   2450          2450       2450    1
KING            5000          7450       7450    2
MILLER          1300          8750       8750    3

ADAMS      20   1100          9850       1100    1
FORD            3000         12850       4100    2
JONES           2975         15825       7075    3
SCOTT           3000         18825      10075    4
SMITH            800         19625      10875    5

ALLEN      30   1600         21225       1600    1
BLAKE           2850         24075       4450    2
JAMES            950         25025       5400    3
MARTIN          1250         26275       6650    4
TURNER          1500         27775       8150    5
WARD            1250         29025       9400    6

Execution Plan

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

   0      SELECT STATEMENT Optimizer=CHOOSE

   1    0   WINDOW (SORT)

   2    1     TABLE ACCESS (FULL) OF 'EMP'

Statistics

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

          0  recursive calls

          0  db block gets

          3  consistent gets

          0  physical reads

          0  redo size

       1658  bytes sent via SQL*Net to client

        503  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          1  sorts (memory)

          0  sorts (disk)

         14  rows processed

 

set autotrace traceonly explain
break on deptno skip 1
column ename format A6
column deptno format 999
column sal format 99999
column seq format 999
SELECT ename "Ename", deptno "Deptno", sal "Sal",
  SUM(sal)
    OVER (ORDER BY deptno, ename) "Running Total",
  SUM(SAL)
    OVER (PARTITION BY deptno
          ORDER BY ename) "Dept Total",
  ROW_NUMBER()
    OVER (PARTITION BY deptno
          ORDER BY ENAME) "Seq"
FROM emp
ORDER BY deptno, ename
/
Ename  Deptno    Sal Running Total Dept Total  Seq
------ ------ ------ ------------- ---------- ----
CLARK      10   2450          2450       2450    1
KING            5000          7450       7450    2
MILLER          1300          8750       8750    3

ADAMS      20   1100          9850       1100    1
FORD            3000         12850       4100    2
JONES           2975         15825       7075    3
SCOTT           3000         18825      10075    4
SMITH            800         19625      10875    5

ALLEN      30   1600         21225       1600    1
BLAKE           2850         24075       4450    2
JAMES            950         25025       5400    3
MARTIN          1250         26275       6650    4
TURNER          1500         27775       8150    5
WARD            1250         29025       9400    6

Execution Plan
---------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   WINDOW (SORT)
   2    1     TABLE ACCESS (FULL) OF 'EMP'
Statistics
---------------------------------------------------
          0 recursive calls
          0 db block gets
          3 consistent gets
          0 physical reads
          0 redo size
       1658 bytes sent via SQL*Net to client
        503 bytes received via SQL*Net from client
          2 SQL*Net roundtrips to/from client
          1 sorts (memory)
          0 sorts (disk)
         14 rows processed

 

 

这个例子展示了如何对整个查询计算动态和。通过SUM(sal) OVER (ORDER BY deptno, ename)来使用排序好的结果集完成计算。

更进一步,我们能够对每一个部门计算动态和,当开始下一个部门时总和重新开始计算。PARTITION BY deptno in that SUM(sal) 用于完成这个目的。在查询中,Partition从句用于将数据分割成多组。

ROW_NUMBER()函数用于对每组数据行根据我们的排序标准指定序列号,将加一个“Seq”列,用来显示这个位置。执行计划表明,整个查询只用3consistent get就完成了,效率很高,而用标准SQL甚至PL/SQL根本不可能。

TOP-N查询

我们如何按照某些字段获取前几条记录呢?

在可以使用这些分析函数之前,这类问题非常难以解答。

然而,遇到使用困难的人们通常认为TOP-N查询仍有一些问题。设计报表时有一些地方需要注意。考虑一下这个似乎合情理的需求吧:我想按部门得到工资前三名的营业代表。

这个需求的问题在于模糊性,因为会存在重复值,可能有四个人都得到了相同的工资,我们该怎么办呢?

 

1

将营业代表按工资从多到少排序,给出前三行。如果某部门中少于三人,则给出少于三个记录。

set autotrace on explain
break on deptno skip 1

SELECT * FROM (
  SELECT deptno, ename, sal, ROW_NUMBER()
  OVER (
    PARTITION BY deptno ORDER BY sal DESC
  ) Top3 FROM emp
)
WHERE Top3 <= 3

/

    DEPTNO ENAME             SAL       TOP3
---------- ---------- ---------- ----------
        10 KING             5000          1
           CLARK            2450          2
           MILLER           1300          3

        20 SCOTT            3000          1
           FORD             3000          2
           JONES            2975          3

        30 BLAKE            2850          1
           ALLEN            1600          2
           TURNER           1500          3

9 rows selected.

Execution Plan
--------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   VIEW
   2    1     WINDOW (SORT)
   3    2       TABLE ACCESS (FULL) OF 'EMP'

 

抱歉!评论已关闭.