Oracle大师Thomas Kyte在他的经典著作中,反复强调过一个实现需求方案选取顺序:

“如果你可以使用一句SQL解决的需求,就使用一句SQL;如果不可以,就考虑PL/SQL是否可以;如果PL/SQL实现不了,就考虑Java存储过程是否可以;如果这些都不可能实现,那么就需要考虑你是否真的需要实现这个需求。”

各个关系型DBMS产品都在遵守关系型数据库模型的基本体系架构,遵循通用的SQL国际规范。同时,为了更好地配合自身数据库实现的特征,以及提供更加丰富的功能,各个DBMS纷纷在标准SQL上进行了一系列语法、语义以及功能上的拓展实现。Oracle从商用化开始,就不断的在这条道路上不断探索。借助Oracle提供的很多拓展SQL语句和函数,可以帮助我们将过去很复杂的需求,通过简单的一句SQL加以实现。

本篇就介绍三个日常开发中有用的函数方法。

1、listagg

我们有时候会遇到这样的需求:“对员工列表进行操作,将每个部门的员工名称横向排列,以逗号进行分割”。

员工表我们使用scott用户schema下的emp表。

SQL> select * from emp;

EMPNO ENAME     JOB        MGR HIREDATE         SAL     COMM DEPTNO

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

7369 SMITH     CLERK     7902 1980-12-17    800.00              20

7876 ADAMS     CLERK     7788 1987-5-23    1100.00              20

7900 JAMES     CLERK     7698 1981-12-3     950.00              30

7902 FORD      ANALYST   7566 1981-12-3    3000.00              20

7934 MILLER    CLERK     7782 1982-1-23    1300.00              10

(篇幅原因,有省略……)

14 rows selected

这个需求的关键在于如何将ename员工名称列压扁为一行数据。如果不使用SQL解决,最直观的想法就是使用PL/SQL进行迭代遍历,获取到所有的数据行记录。

此时,我们就可以求助Oracle 11g中的函数listagg。首先我们来看一下listagg的函数描述(摘自Oracle SQL Reference)。

listagg的作用是将分组范围内的所有行特定列的记录加以合并成行。函数签名中的measure_expr为分组中每个列的表达式,而delimiter为合并分割符。如果delimiter不设置的话,就表示无分割符。

中间within group后面的order_by_clause表示的是进行合并中要遵守的排序顺序。而后面的over子句表明listagg是具有分析函数analyze funcation特性的。具体采用listagg有三个场景。

ü       当无分组的single-list情况下

如果要获取到deptno为30的所有员工横行记录。

SQL> select * from emp where deptno=30;

EMPNO ENAME     JOB        MGR HIREDATE         SAL     COMM DEPTNO

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

7499 ALLEN     SALESMAN  7698 1981-2-20    1600.00   300.00    30

7521 WARD      SALESMAN  7698 1981-2-22    1250.00   500.00    30

7654 MARTIN    SALESMAN  7698 1981-9-28    1250.00  1400.00    30

7698 BLAKE     MANAGER   7839 1981-5-1     2850.00              30

7844 TURNER    SALESMAN  7698 1981-9-8     1500.00     0.00    30

7900 JAMES     CLERK     7698 1981-12-3     950.00              30

6 rows selected

--按照empno进行排序

SQL> select listagg(ename,' , ') within group (order byempno) from emp where deptno=30;

LISTAGG(ENAME,',')WITHINGROUP(

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

ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES

ü       在有分组条件下的listagg使用

如果要使用分组统计各个部门的所有员工列表。

SQL> select deptno, listagg(ename,' ,') within group (order by empno) from emp group by deptno;

DEPTNO LISTAGG(ENAME,',')WITHINGROUP(

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

10 CLARK ,KING ,MILLER

20 SMITH ,JONES ,SCOTT ,ADAMS ,FORD

30 ALLEN ,WARD ,MARTIN ,BLAKE ,TURNER ,JAMES

ü       使用over分组情况

如果要统计所有工作十年以上员工和他们相同部门的员工信息,就需要在listagg的基础上加入over分析函数子句。

SQL> select deptno, ename, listagg(ename, ' , ') within group (order by empno)

2 over (partition by deptno) as emp_list

3 from emp

4 where hiredate<=add_months(sysdate,-10*12);

DEPTNO ENAME     EMP_LIST

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

10 CLARK     CLARK , KING , MILLER

10 KING      CLARK , KING , MILLER

10 MILLER    CLARK , KING , MILLER

20 SMITH     SMITH , JONES , SCOTT , ADAMS , FORD

20 JONES     SMITH , JONES , SCOTT , ADAMS , FORD

20 SCOTT     SMITH , JONES , SCOTT , ADAMS , FORD

20 ADAMS     SMITH , JONES , SCOTT , ADAMS , FORD

20 FORD      SMITH , JONES , SCOTT , ADAMS , FORD

30 ALLEN     ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES

30 WARD      ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES

30 MARTIN    ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES

30 BLAKE     ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES

30 TURNER    ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES

30 JAMES     ALLEN , WARD , MARTIN , BLAKE , TURNER , JAMES

14 rows selected

2、lag函数“取到上个月的销售额”

我们在进行销售数据统计汇总时候,经常遇到这样的需求:“对比上月(上季度同月份或者上年度同月份),我们的销售变化情况如何?”。我们的销售数据通常是对应单月信息,如下所示。

SQL> select * from sales_qual;

MONT        QUALITIES PRICE

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

2011-01          1000 23.40

2011-02          1020 23.40

2011-03          1030 33.40

2011-04          1035 10.30

如果要获取到之前月份的信息,没有SQL专门函数就意味着需要使用PL/SQL代码进行反复的迭代获取。现在,我们可以使用lag函数来轻易实现这个功能。

lag函数是一个典型的分析函数。它提供了在不使用自连接的情况下,访问多个数据行的能力。在返回多个结果行的时候,lag函数可以访问到向上特定offset偏移行的数据。

value_expr就是访问到向上数据行进行的操作。offset是返回偏移的函数,默认值为1。over中,可以定义内部分析的顺序列。

如果我们要获取到对应上个月的销售数据,SQL语句如下:

SQL> select mont,qualities, lag(qualities,1) over (order by mont) as "Next Month Qual"

2 from sales_qual

3 order by mont;

MONT        QUALITIES Next Month Qual

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

2011-01          1000

2011-02          1020           1000

2011-03          1030           1020

2011-04          1035           1030

之后对销量变化率的处理就方便了,可以进行增长率比对等操作。那么,如果是上一年度或者上一季度的数据呢?我们只需要调节offset,从1变化为12或者3就可以了。

最后,对ignore/respect nulls子句的使用是什么呢?该子句的作用是确定当value_expr表达式计算出的数值为空null的时候,该列如何进行计算。ignore nulls的作用就是忽略上面计算为空的行,采用上上行row的计算结果。respect nulls的作用是直接反映为null。respect nulls为默认值。

SQL> select * from sales_qual;

MONT        QUALITIES PRICE

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

2011-04          1035 10.30

2011-05                12.30

2011-06

6 rows selected

SQL> select mont,qualities, lag(qualities,1)ignore nullsover (order by mont) as "Next Month Qual"

2 from sales_qual

3 order by mont;

MONT        QUALITIES Next Month Qual

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

2011-04          1035           1030

2011-05                          1035

2011-06                          1035

6 rows selected

SQL> select mont,qualities, lag(qualities,1) respect nulls over (order by mont) as "Next Month Qual"

2 from sales_qual

3 order by mont;

MONT        QUALITIES Next Month Qual

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

2011-04          1035           1030

2011-05                          1035

2011-06

6 rows selected

3、lead函数获取下一个月销售量

有lag的获取上个offset处理行的函数,就有lead函数处理下一个处理行的函数。lead函数实际上就是lag的逆向过程。

相关各项参数与lag函数的相同。区别就在于lead函数获取的是排序后结果集合的后offset数据行记录。

SQL> select mont,qualities, lead(qualities,1) over (order by mont) as "Next Month Qual"

2 from sales_qual

3 order by mont;

MONT        QUALITIES Next Month Qual

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

2011-01          1000           1020

2011-02          1020           1030

2011-03          1030           1035

2011-04          1035

4、结论

Oracle预定义函数,特别是分析函数中,有很多是非常优秀使用而且功能强大的。熟悉这个函数,在特定的环境场景下使用这些函数,可以帮我们迅速的解决很头疼的一些SQL场景。而且Oracle预定义函数的效率上也是令人满意的。

oracle listagg函数、lag函数、lead函数 实例的更多相关文章

  1. ORACLE lag()与lead() 函数

    一.简介 lag与lead函数是跟偏移量相关的两个分析函数,通过这两个函数可以在一次查询中取出同一字段的前N行的数据(lag)和后N行的数据(lead)作为独立的列,从而更方便地进行进行数据过滤.这种 ...

  2. KingbaseES lag 和 lead 函数

    1.简介 lag与lead函数是跟偏移量相关的两个分析函数,通过这两个函数可以在一次查询中取出同一字段的前N行的数据(lag)和后N行的数据(lead)作为独立的列,从而更方便地进行进行数据过滤. 2 ...

  3. pandas实现hive的lag和lead函数 以及 first_value和last_value函数

    lag和lead VS shift 该函数的格式如下: 第一个参数为列名, 第二个参数为往上第n行(可选,默认为1), 第三个参数为默认值(当往上第n行为NULL时候,取默认值,如不指定,则为NULL ...

  4. Oracle 分析函数之 lag和lead

    Lag和Lead分析函数可以在同一次查询中取出同一字段的前N行的数据(Lag)和后N行的数据(Lead)作为独立的列. 这种操作可以代替表的自联接,并且LAG和LEAD有更高的效率. /*语法*/   ...

  5. oracle函数 lag()和lead()

    [语法] lag(EXPR,<OFFSET>,<DEFAULT>) LEAD(EXPR,<OFFSET>,<DEFAULT>) [功能]表示根据COL1 ...

  6. oralce的lag和lead函数

    https://www.cnblogs.com/always-online/p/5010185.html

  7. oracle lag与lead分析函数简介

    lag与lead函数是跟偏移量相关的两个分析函数,通过这两个函数我们可以取到当前行列的偏移N行列的值 lag可以看着是正的向上的偏移 lead可以认为负的向下的偏移 具体我们来看几个例子: 我们先看下 ...

  8. Oracle-分析函数之取上下行数据lag()和lead()

    这两个函数是偏移量函数,可以查出一个字段的上一个值或者下一个值,配合over来使用. lead函数,这个函数是向上偏移. lag函数是向下偏移一位. 语法 [语法] lag(EXPR,<OFFS ...

  9. oracle listagg和wm_concat函数

    对于将一列多值合并成一行问题,oracle提供了wmsys.wm_concat和listagg函数处理此问题,下面我们以emp表中数据为例,看看两函数的使用方法 假设我们需要统计每种job下面有哪些员 ...

随机推荐

  1. sql server 存储过程基础知识

    转自家园大哥博文http://www.cnblogs.com/jiajiayuan/archive/2011/06/15/2081201.html 什么是存储过程呢?存储过程就是作为可执行对象存放在数 ...

  2. AngularJS------报错"The selector "app-user-item" did not match any elements"

    原因:新建的组件没有在任何界面使用到 解决方法:在界面使用该组件

  3. SpringBoot(十一)-- 动态数据源

    SpringBoot中使用动态数据源可以实现分布式中的分库技术,比如查询用户 就在用户库中查询,查询订单 就在订单库中查询. 一.配置文件application.properties # 默认数据源 ...

  4. MySQL 安装与配置

    Linux 安装 MySQL Windows 安装 MySQL 如何连接 MySQL 如何修改 MySQL 密码 如何重置 MySQL 密码

  5. 使用pyenv管理不同的python版本

    1. pvenv的安装 git clone https://github.com/yyuu/pyenv.git ~/.pyenv echo 'export PYENV_ROOT="$HOME ...

  6. lua元表(metatable)和元方法(metamethod)

    (一) 元表概念: 引言:Lua中的每个值都有一套预定义的操作集合,如数字相加等.但无法将两个table相加,此时可通过元表修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定操作. 访问机制 ...

  7. 一个汉字转拼音的php类

    代码来自网上,可用 <?php function Pinyin($_String, $_Code='gb2312') { $_DataKey = "a|ai|an|ang|ao|ba| ...

  8. Python进阶 学习笔记(三)

    (涉及内容:定制类) __str__和__repr__ 如果要把一个类的实例变成 str,就需要实现特殊方法__str__(): class Person(object): def __init__( ...

  9. Mybatis的resultMap返回map

    <resultMap type="Map" id="bankMaintainMap"> <result column="bank_n ...

  10. thinkphp3.2 实现二级导航和高亮显示

    一.控制器代码如下: public function index(){ //高亮显示 $action = CONTROLLER_NAME."/".ACTION_NAME; $thi ...