IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> Oracle Database 21c 分析函数增强 -> 正文阅读

[大数据]Oracle Database 21c 分析函数增强

大家好,我是只谈技术不剪发的 Tony 老师。今天给大家分享一下 Oracle database 21c 中分析函数的语法增强,包括 WINDOW 子句、GROUPS 子句以及 EXCLUDE 子句。

数据初始化

本文介绍的案例将会使用以下示例表:

create table emp (
  empno    number(4) constraint pk_emp primary key,
  ename    varchar2(10),
  job      varchar2(9),
  mgr      number(4),
  hiredate date,
  sal      number(7,2),
  comm     number(7,2),
  deptno   number(2) );

insert into emp values (7369,'SMITH','CLERK',7902,to_date('17-12-1980','dd-mm-yyyy'),800,NULL,20);
insert into emp values (7499,'ALLEN','SALESMAN',7698,to_date('20-2-1981','dd-mm-yyyy'),1600,300,30);
insert into emp values (7521,'WARD','SALESMAN',7698,to_date('22-2-1981','dd-mm-yyyy'),1250,500,30);
insert into emp values (7566,'JONES','MANAGER',7839,to_date('2-4-1981','dd-mm-yyyy'),2975,NULL,20);
insert into emp values (7654,'MARTIN','SALESMAN',7698,to_date('28-9-1981','dd-mm-yyyy'),1250,1400,30);
insert into emp values (7698,'BLAKE','MANAGER',7839,to_date('1-5-1981','dd-mm-yyyy'),2850,NULL,30);
insert into emp values (7782,'CLARK','MANAGER',7839,to_date('9-6-1981','dd-mm-yyyy'),2450,NULL,10);
insert into emp values (7788,'SCOTT','ANALYST',7566,to_date('13-JUL-87','dd-mm-rr')-85,3000,NULL,20);
insert into emp values (7839,'KING','PRESIDENT',NULL,to_date('17-11-1981','dd-mm-yyyy'),5000,NULL,10);
insert into emp values (7844,'TURNER','SALESMAN',7698,to_date('8-9-1981','dd-mm-yyyy'),1500,0,30);
insert into emp values (7876,'ADAMS','CLERK',7788,to_date('13-JUL-87', 'dd-mm-rr')-51,1100,NULL,20);
insert into emp values (7900,'JAMES','CLERK',7698,to_date('3-12-1981','dd-mm-yyyy'),950,NULL,30);
insert into emp values (7902,'FORD','ANALYST',7566,to_date('3-12-1981','dd-mm-yyyy'),3000,NULL,20);
insert into emp values (7934,'MILLER','CLERK',7782,to_date('23-1-1982','dd-mm-yyyy'),1300,NULL,10);
commit;

create table t1 (id number, value number);

insert into t1 (id, value) values (1, 1);
insert into t1 (id, value) values (2, 2);
insert into t1 (id, value) values (3, 3);
insert into t1 (id, value) values (4, 3);
insert into t1 (id, value) values (5, 4);
insert into t1 (id, value) values (6, 6);
insert into t1 (id, value) values (7, 6);
insert into t1 (id, value) values (8, 7);
insert into t1 (id, value) values (9, 7);
insert into t1 (id, value) values (10, 8);
commit;

WINDOW 子句

在以前的版本中,窗口选项属于分析函数调用参数的一部分。以下查询使用 FIRST_VALUE 分析函数返回了每个部门中的最低月薪:

select empno,
       deptno,
       sal,
       first_value(sal)
         over (partition by deptno order by sal) as lowest_in_dept
from   emp;

     EMPNO     DEPTNO        SAL LOWEST_IN_DEPT
---------- ---------- ---------- --------------
      7934         10       1300           1300
      7782         10       2450           1300
      7839         10       5000           1300
      7369         20        800            800
      7876         20       1100            800
      7566         20       2975            800
      7788         20       3000            800
      7902         20       3000            800
      7900         30        950            950
      7654         30       1250            950
      7521         30       1250            950
      7844         30       1500            950
      7499         30       1600            950
      7698         30       2850            950

从 Oracle 21c 开始,窗口选项可以使用单独的 window 子句进行定义,然后在分析函数调用中使用定义好的变量。例如:

select empno,
       deptno,
       sal,
       first_value(sal) over w1 as lowest_in_dept
from   emp
window w1 as (partition by deptno order by sal);

     EMPNO     DEPTNO        SAL LOWEST_IN_DEPT
---------- ---------- ---------- --------------
      7934         10       1300           1300
      7782         10       2450           1300
      7839         10       5000           1300
      7369         20        800            800
      7876         20       1100            800
      7566         20       2975            800
      7788         20       3000            800
      7902         20       3000            800
      7900         30        950            950
      7654         30       1250            950
      7521         30       1250            950
      7844         30       1500            950
      7499         30       1600            950
      7698         30       2850            950

这种法语方式意味着多个分析函数可以使用相同的窗口子句。下面的示例增加了一个 RANK 分析函数,基于员工在部门内的月薪从低到高进行排名:

select empno,
       deptno,
       sal,
       first_value(sal) over w1 as lowest_in_dept,
       rank() over w1 as sal_rank_in_dept
from   emp
window w1 as (partition by deptno order by sal);

     EMPNO     DEPTNO        SAL LOWEST_IN_DEPT SAL_RANK_IN_DEPT
---------- ---------- ---------- -------------- ----------------
      7934         10       1300           1300                1
      7782         10       2450           1300                2
      7839         10       5000           1300                3
      7369         20        800            800                1
      7876         20       1100            800                2
      7566         20       2975            800                3
      7788         20       3000            800                4
      7902         20       3000            800                4
      7900         30        950            950                1
      7654         30       1250            950                2
      7521         30       1250            950                2
      7844         30       1500            950                4
      7499         30       1600            950                5
      7698         30       2850            950                6

我们可以定义和使用多个不同的窗口选项。以下示例查询了每个部门中的最低月薪和最高月薪,查询中 使用了两次 FIRST_VALUE 分析函数,每次指定的窗口选项不同:

select empno,
       deptno,
       sal,
       first_value(sal) over w1 as lowest_in_dept,
       first_value(sal) over w2 as highest_in_dept
from   emp
window w1 as (partition by deptno order by sal),
       w2 as (partition by deptno order by sal desc);

     EMPNO     DEPTNO        SAL LOWEST_IN_DEPT HIGHEST_IN_DEPT
---------- ---------- ---------- -------------- ---------------
      7934         10       1300           1300            5000
      7782         10       2450           1300            5000
      7839         10       5000           1300            5000
      7369         20        800            800            3000
      7876         20       1100            800            3000
      7566         20       2975            800            3000
      7788         20       3000            800            3000
      7902         20       3000            800            3000
      7900         30        950            950            2850
      7521         30       1250            950            2850
      7654         30       1250            950            2850
      7844         30       1500            950            2850
      7499         30       1600            950            2850
      7698         30       2850            950            2850

我们也可以定义一个通用的 WINDOW 子句,然后在分析函数调用中扩展该变量:

select row_number () over w1 as row_order,
       sal,
       avg(sal) over (w1 rows between unbounded preceding and current row) as avg_rolling,
       avg(sal) over (w1 rows between unbounded preceding and unbounded following) as avg_all
from   emp
window w1 as (order by sal);

 ROW_ORDER        SAL AVG_ROLLING    AVG_ALL
---------- ---------- ----------- ----------
         1        800         800 2073.21429
         2        950         875 2073.21429
         3       1100         950 2073.21429
         4       1250        1025 2073.21429
         5       1250        1070 2073.21429
         6       1300  1108.33333 2073.21429
         7       1500  1164.28571 2073.21429
         8       1600     1218.75 2073.21429
         9       2450  1355.55556 2073.21429
        10       2850        1505 2073.21429
        11       2975  1638.63636 2073.21429
        12       3000  1752.08333 2073.21429
        13       3000  1848.07692 2073.21429
        14       5000  2073.21429 2073.21429

这种新的语法是一种查询转换(query transformation)。 我们可以通过会话跟踪查看相关信息:

select value from v$diag_info where  name = 'Default Trace File';

VALUE
----------------------------------------------------------------
/u01/app/oracle/diag/rdbms/cdb1/cdb1/trace/cdb1_ora_23984.trc

1 row selected.

执行语句的 10053 跟踪:

alter session set events '10053 trace name context forever';

select empno,
       deptno,
       sal,
       first_value(sal) over w1 as lowest_in_dept,
       first_value(sal) over w2 as highest_in_dept
from   emp
window w1 as (partition by deptno order by sal),
       w2 as (partition by deptno order by sal desc);
       
alter session set events '10053 trace name context off';

跟踪文件中以“Final query after transformations”开始的部分显示了执行查询转换之后实际执行的语句:

Final query after transformations:******* UNPARSED QUERY IS *******
SELECT "EMP"."EMPNO" "EMPNO",
       "EMP"."DEPTNO" "DEPTNO",
       "EMP"."SAL" "SAL",
       FIRST_VALUE("EMP"."SAL") OVER ( PARTITION BY "EMP"."DEPTNO" ORDER BY "EMP"."SAL"
         RANGE  BETWEEN  UNBOUNDED  PRECEDING  AND  CURRENT ROW ) "LOWEST_IN_DEPT",
       FIRST_VALUE("EMP"."SAL") OVER ( PARTITION BY "EMP"."DEPTNO" ORDER BY "EMP"."SAL" DESC
         RANGE  BETWEEN  UNBOUNDED  PRECEDING  AND  CURRENT ROW ) "HIGHEST_IN_DEPT"
FROM "TESTUSER1"."EMP" "EMP"

从以上结果可以看出,示例语句被重写成了 Oracle 21c 之前可以执行的语法。

GROUPS 子句

除了 ROWS 和 RANGE 关键字之外,新版本增加了 GROUPS 关键字,因此定义窗口子句的三个选项如下:

  • ROWS:以行为单位指定距离当前行的窗口,不考虑取值相同的更多数据行。排序字段数量不限。
  • RANGE:基于当前行的某个字段值指定一个取值范围。因此,在创建排序集合之前,系统不确定窗口包含多少行数据。此时需要有一个排序字段,而且字段类型能够支持加减运算,例如数字、日期或者时间间隔。
  • GROUPS:基于排序字段将数据分为多个组,等值行属于相同的组。当前行属于当前组,所以 preceding 或者 following 表示前面的组或者后面的组,而不是具体的行。与 RANGE 选项类似,在创建排序集合之前,系统不确定窗口包含多少行数据。排序字段数量不限。

以下查询比较了使用 ROWS、GROUPS 以及 RANGE 选项计算“between 1 preceding and current row”平均月薪的结果:

select row_number () over (order by sal) as row_order,
       sal,
       avg(sal) over w1 as avg_rows,
       avg(sal) over w2 as avg_groups,
       avg(sal) over w3 as avg_range
from   emp
window w1 as (order by sal rows between 1 preceding and current row),
       w2 as (order by sal groups between 1 preceding and current row),
       w3 as (order by sal range between 1 preceding and current row);

 ROW_ORDER        SAL   AVG_ROWS AVG_GROUPS  AVG_RANGE
---------- ---------- ---------- ---------- ----------
         1        800        800        800        800
         2        950        875        875        950
         3       1100       1025       1025       1100
         4       1250       1175       1200       1250
         5       1250       1250       1200       1250
         6       1300       1275 1266.66667       1300
         7       1500       1400       1400       1500
         8       1600       1550       1550       1600
         9       2450       2025       2025       2450
        10       2850       2650       2650       2850
        11       2975     2912.5     2912.5       2975
        12       3000     2987.5 2991.66667       3000
        13       3000       3000 2991.66667       3000
        14       5000       4000 3666.66667       5000

AVG_ROWS 列返回了当前行和前一行的平均月薪。

AVG_GROUPS 列开始的结果和 AVG_ROWS 相同。但是对于第 4 行,存在一个等值行,第 4 行和第 5 行属于当前组,第 3 行属于前一组。所以返回的不是第 3 行和第 4 行的平均值,而是第 3 行、第 4 行以及第 5 行的平均值。

AVG_RANGE 列返回的是当前行对应的月薪。因为 RANGE 是按照数值范围,而不是行的数量指定窗口。“1 preceding”表示的是数值减 1。

下面是一个类似的查询。数据表 T1 中包含了间隔为 1 的数值,以及一些等值行和跳跃行,因为没有数字 5。

select row_number () over (order by value) as row_order,
       value,
       avg(value) over w1 as avg_rows,
       avg(value) over w2 as avg_groups,
       avg(value) over w3 as avg_range
from   t1
window w1 as (order by value rows between 1 preceding and current row),
       w2 as (order by value groups between 1 preceding and current row),
       w3 as (order by value range between 1 preceding and current row);

 ROW_ORDER      VALUE   AVG_ROWS AVG_GROUPS  AVG_RANGE
---------- ---------- ---------- ---------- ----------
         1          1          1          1          1
         2          2        1.5        1.5        1.5
         3          3        2.5 2.66666667 2.66666667
         4          3          3 2.66666667 2.66666667
         5          4        3.5 3.33333333 3.33333333
         6          6          5 5.33333333          6
         7          6          6 5.33333333          6
         8          7        6.5        6.5        6.5
         9          7          7        6.5        6.5
        10          8        7.5 7.33333333 7.33333333

AVG_ROWS 列返回了当前行和前一行的平均值。

AVG_GROUPS 列的第 3 行返回了不同的结果。第 3 行和第 4 行是等值行,它们是一个组,第 2 行是前一组。因此第 3 行和第 4 行的结果是 2、3、4 行的平均值。

AVG_RANGE 列和 AVG_GROUPS 列类似,但是遇到跳跃数值时不同。第 6 行和第 7 行是等值行,窗口范围从 6-1=5 开始,但是数据集中没有数字 5,所以第 6 行和第 7 行的结果只有当前行的值(6)。

EXCLUDE 子句

EXCLUDE 子句支持不同的选项,用于排除窗口内的某些数据行。

  • EXCLUDE NO OTHERS:不排除任何数据。这种方式等价于不指定 EXCLUDE 选项,因此是默认行为。
  • EXCLUDE CURRENT ROW:排除当前行。
  • EXCLUDE GROUP:排除当前组、当前行以及其他数值相同的其他数据行。
  • EXCLUDE TIES:排除数值和当前行相同的其他数据行,但是保留当前行。

以上排除选项都可以用于 ROWS、RANGE 以及 GROUPS 窗口定义。

以下查询针对每行数据计算前一行和后一行的平均值,但是不包含当前行:

select row_number () over w1 as row_order,
       value,
       avg(value) over (w1 rows between 1 preceding and 1 following exclude current row) as ex_current_row
from t1
window w1 as (order by value);

 ROW_ORDER      VALUE EX_CURRENT_ROW
---------- ---------- --------------
         1          1              2
         2          2              2
         3          3            2.5
         4          3            3.5
         5          4            4.5
         6          6              5
         7          6            6.5
         8          7            6.5
         9          7            7.5
        10          8              7
SQL>

以下查询针对每行数据计算前一行和后一行的平均值,但是不包含当前组。在窗口定义中使用 ROWS,同时在排除选项中使用组,可以返回一个有趣的结果:

select row_number () over w1 as row_order,
       value,
       avg(value) over (w1 rows between 1 preceding and 1 following exclude group) as ex_group
from t1
window w1 as (order by value);

 ROW_ORDER      VALUE   EX_GROUP
---------- ---------- ----------
         1          1          2
         2          2          2
         3          3          2
         4          3          4
         5          4        4.5
         6          6          4
         7          6          7
         8          7          6
         9          7          8
        10          8          7

以下查询基于 unbounded preceding 和 current row 范围计算移动平均值,同时排除了等值行,所以报告的范围和通常包含当前行的等值行不同:

select row_number () over w1 as row_order,
       value,
       avg(value) over (w1 range between unbounded preceding and current row exclude ties) as ex_ties
from t1
window w1 as (order by value);

 ROW_ORDER      VALUE    EX_TIES
---------- ---------- ----------
         1          1          1
         2          2        1.5
         3          3          2
         4          3          2
         5          4        2.6
         6          6 3.16666667
         7          6 3.16666667
         8          7          4
         9          7          4
        10          8        4.7

关于 Oracle 分析函数的语法和更多示例,可以参考官方文档

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-05-05 11:25:14  更:2022-05-05 11:29:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 1:24:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码