DB2 分组查询语句ROW_NUMBER() OVER() (转载)
说起 DB2 在线分析处理,可以用很好很强大来形容。这项功能特别适用于各种统计查询,这些查询用通常的SQL很难实现,或者根本就无发实现。首先,我们从一个简单的例子开始,来一步一步揭开它神秘的面纱,请看下面的SQL:
- SELECT
- ROW_NUMBER() OVER(ORDER BY SALARY) AS 序号,
- NAME AS 姓名,
- DEPT AS 部门,
- SALARY AS 工资
- FROM
- (
- --姓名 部门 工资
- VALUES
- ('张三','市场部',4000),
- ('赵红','技术部',2000),
- ('李四','市场部',5000),
- ('李白','技术部',5000),
- ('王五','市场部',NULL),
- ('王蓝','技术部',4000)
- ) AS EMPLOY(NAME,DEPT,SALARY);
- 查询结果如下:
- 序号 姓名 部门 工资
- 1 赵红 技术部 2000
- 2 张三 市场部 4000
- 3 王蓝 技术部 4000
- 4 李四 市场部 5000
- 5 李白 技术部 5000
- 6 王五 市场部 (null)
看到上面的ROW_NUMBER() OVER()了吗?很多人非常不理解,怎么两个函数能这么写呢?甚至有人怀疑上面的SQL语句是不是真的能执行。其实,ROW_NUMBER是个函数没错,它的作用从它的名字也可以看出来,就是给查询结果集编号。但是,OVER并不是一个函数,而是一个表达式,它的作用是定义一个作用域(或者可以说是结果集),OVER前面的函数只对OVER定义的结果集起作用。怎么样,不明白?没关系,我们后面还会详细介绍。
从上面的SQL我们可以看出,典型的 DB2 在线分析处理的格式包括两部分:函数部分和OVER表达式部分。那么,函数部分可以有哪些函数呢?如下:
- ROW_NUMBER
- RANK
- DENSE_RANK
- FIRST_VALUE
- LAST_VALUE
- LAG
- LEAD
- COUNT
- MIN
- MAX
- AVG
- SUM
上面这些函数的作用,我会在后面逐步给大家介绍,大家可以根据函数名猜测一下函数的作用。
假设我想在不改变上面语句的查询结果的情况下,追加对部门员工的平均工资和全体员工的平均工资的查询,怎么办呢?用通常的SQL很难查询,但是用OLAP函数则非常简单,如下SQL所示:
- SELECT
- ROW_NUMBER() OVER() AS 序号,
- ROW_NUMBER() OVER(PARTITION BY DEPT ORDER BY SALARY) AS 部门序号,
- NAME AS 姓名,
- DEPT AS 部门,
- SALARY AS 工资,
- AVG(SALARY) OVER(PARTITION BY DEPT) AS 部门平均工资,
- AVG(SALARY) OVER() AS 全员平均工资
- FROM
- (
- --姓名 部门 工资
- VALUES
- ('张三','市场部',4000),
- ('赵红','技术部',2000),
- ('李四','市场部',5000),
- ('李白','技术部',5000),
- ('王五','市场部',NULL),
- ('王蓝','技术部',4000)
- ) AS EMPLOY(NAME,DEPT,SALARY);
- 查询结果如下:
- 序号 部门序号 姓名 部门 工资 部门平均工资 全员平均工资
- 1 1 张三 市场部 4000 4500 4000
- 2 2 李四 市场部 5000 4500 4000
- 3 3 王五 市场部 (null) 4500 4000
- 4 1 赵红 技术部 2000 3666 4000
- 5 2 王蓝 技术部 4000 3666 4000
- 6 3 李白 技术部 5000 3666 4000
请注意序号和部门序号之间的区别,我们在查询部门序号的时候,在OVER表达式中多了两个子句,分别是PARTITION BY 和ORDER BY。它们有什么作用呢?在介绍它们的作用之前,我们先来回顾一下OVER的作用,还记得吗?
OVER是一个表达式,它的作用是定义一个作用域(或者可以说是结果集),OVER前面的函数只对OVER定义的结果集起作用。
ORDER BY的作用大家应该非常熟悉,用来对结果集排序。PARTITION BY的作用其实也很简单,和GROUP BY 的作用相同,用来对结果集分组。
到此为止,大家应该对OLAP函数的套路有一定的了解和体会了吧。大家看一下上面SQL的结果集,发现王五的工资是null,当我们按工资排序时,null被放到最后,我们想把null放在前边该怎么办呢?使用NULLS FIRST关键字即可,默认是NULLS LAST,请看下面的SQL:
- SELECT
- ROW_NUMBER() OVER(ORDER BY SALARY desc NULLS FIRST) AS RN,
- RANK() OVER(ORDER BY SALARY desc NULLS FIRST) AS RK,
- DENSE_RANK() OVER(ORDER BY SALARY desc NULLS FIRST) AS D_RK,
- NAME AS 姓名,
- DEPT AS 部门,
- SALARY AS 工资
- FROM
- (
- --姓名 部门 工资
- VALUES
- ('张三','市场部',4000),
- ('赵红','技术部',2000),
- ('李四','市场部',5000),
- ('李白','技术部',5000),
- ('王五','市场部',NULL),
- ('王蓝','技术部',4000)
- ) AS EMPLOY(NAME,DEPT,SALARY);
- 查询结果如下:
- RN RK D_RK 姓名 部门 工资
- 1 1 1 王五 市场部 (null)
- 2 2 2 李四 市场部 5000
- 3 2 2 李白 技术部 5000
- 4 4 3 张三 市场部 4000
- 5 4 3 王蓝 技术部 4000
- 6 6 4 赵红 技术部 2000
请注意ROW_NUMBER和RANK之间的区别,RANK是等级,排名的意思,李四和李白的工资都是5000,他们并列排名第二。张三和王蓝的工资都是4000,怎么RANK函数的排名是第四,而DENSE_RANK的排名是第三呢?这正是这两个函数之间的区别。由于有两个第二名,所以RANK函数默认没有第三名。
现在又有个新问题,假设让你查询一下每个员工的工资以及工资小于他的所有员工的平均工资,该怎么办呢?怎么?没听明白问题?不要紧,请看下面的SQL:
- SELECT
- NAME AS 姓名,
- SALARY AS 工资,
- SUM(SALARY) OVER(ORDER BY SALARY NULLS FIRST ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS 小于本人工资的总额,
- SUM(SALARY) OVER(ORDER BY SALARY NULLS FIRST ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS 大于本人工资的总额,
- SUM(SALARY) OVER(ORDER BY SALARY NULLS FIRST ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS 工资总额1,
- SUM(SALARY) OVER() AS 工资总额2
- FROM
- (
- --姓名 部门 工资
- VALUES
- ('张三','市场部',4000),
- ('赵红','技术部',2000),
- ('李四','市场部',5000),
- ('李白','技术部',5000),
- ('王五','市场部',NULL),
- ('王蓝','技术部',4000)
- ) AS EMPLOY(NAME,DEPT,SALARY);
- 查询结果如下:
- 姓名 工资 小于本人工资的总额 大于本人工资的总额 工资总额1 工资总额2
- 王五 (null) (null) 20000 20000 20000
- 赵红 2000 2000 20000 20000 20000
- 张三 4000 6000 18000 20000 20000
- 王蓝 4000 10000 14000 20000 20000
- 李四 5000 15000 10000 20000 20000
- 李白 5000 20000 5000 20000 20000
上面SQL 中的OVER部分出现了一个ROWS子句,我们先来看一下ROWS子句的结构:
- ROWS BETWEEN <上限条件> AND <下限条件>
- 其中“上限条件”可以是如下关键字:
- UNBOUNDED PRECEDING
- <number> PRECEDING
- CURRENT ROW
- “下线条件”可以是如下关键字:
- CURRENT ROW
- <number> FOLLOWING
- UNBOUNDED FOLLOWING
注意,以上关键字都是相对当前行的,UNBOUNDED PRECEDING表示当前行前面的所有行,也就是说没有上限;<number> PRECEDING表示从当前行开始到它前面的<number>行为止,例如,number=2,表示的是当前行前面的2行;CURRENT ROW表示当前行。至于其它两个关键字,我想,不用我说,你也应该知道了吧。如果你还不明白,请仔细分析上面SQL的查询结果。
OVER表达式还可以有个子句,那就是RANGE,它的使用方式和ROWS 十分相似,或者说一模一样,作用也差多不,不过有点区别,如下所示:
RANGE BETWEEN <上限条件> AND <下限条件>
其中的<上限条件> 、<下限条件>和ROWS一模一样,如下的SQL演示它们之间的区别:
- SELECT
- NAME AS 姓名,
- DEPT AS 部门,
- SALARY AS 工资,
- FIRST_VALUE(SALARY, 'IGNORE NULLS') OVER(PARTITION BY DEPT) AS 部门最低工资,
- LAST_VALUE(SALARY, 'RESPECT NULLS') OVER(PARTITION BY DEPT) AS 部门最高工资,
- SUM(SALARY) OVER(ORDER BY SALARY ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS ROWS,
- SUM(SALARY) OVER(ORDER BY SALARY RANGE BETWEEN 500 PRECEDING AND 500 FOLLOWING) AS RANGE
- FROM
- (
- --姓名 部门 工资
- VALUES
- ('张三','市场部',2000),
- ('赵红','技术部',2400),
- ('李四','市场部',3000),
- ('李白','技术部',3200),
- ('王五','市场部',4000),
- ('王蓝','技术部',5000)
- ) AS EMPLOY(NAME,DEPT,SALARY);
- 查询结果如下:
- 姓名 部门 工资 部门最低工资 部门最高工资 ROWS RANGE
- 张三 市场部 2000 2000 4000 4400 4400
- 赵红 技术部 2400 2400 5000 7400 4400
- 李四 市场部 3000 2000 4000 8600 6200
- 李白 技术部 3200 2400 5000 10200 6200
- 王五 市场部 4000 2000 4000 12200 4000
- 王蓝 技术部 5000 2400 5000 9000 5000
上面SQL的RANGE 子句的作用是定义一个工资范围,这个范围的上限是当前行的工资-500,下限是当前行工资+500。例如:李四的工资是3000,所以上限是3000-500=2500,下限是3000+500=3500,那么有谁的工资在2500-3500这个范围呢?只有李四和李白,所以RANGE列的值就是3000(李四)+3200(李白)=6200。以上就是ROWS和RANGE得区别。
上面的SQL 还用到了FIRST_VALUE和LAST_VALUE两个函数,它们的作用也非常简单,用来求OVER 定义集合的最小值和最大值。值得注意的是这两个函数有个参数,'IGNORE NULLS' 或 'RESPECT NULLS',它们的作用正如它们的名字一样,用来忽略NULL值和考虑NULL值。
还有两个函数我们没有介绍,LAG和LEAD,这两个函数的功能非常强大,请看下面SQL:
- SELECT
- NAME AS 姓名,
- SALARY AS 工资,
- LAG(SALARY,0) OVER(ORDER BY SALARY) AS LAG0,
- LAG(SALARY) OVER(ORDER BY SALARY) AS LAG1,
- LAG(SALARY,2) OVER(ORDER BY SALARY) AS LAG2,
- LAG(SALARY,3,0,'IGNORE NULLS') OVER(ORDER BY SALARY) AS LAG3,
- LAG(SALARY,4,-1,'RESPECT NULLS') OVER(ORDER BY SALARY) AS LAG4,
- LEAD(SALARY) OVER(ORDER BY SALARY) AS LEAD
- FROM
- (
- --姓名 部门 工资
- VALUES
- ('张三','市场部',2000),
- ('赵红','技术部',2400),
- ('李四','市场部',3000),
- ('李白','技术部',3200),
- ('王五','市场部',4000),
- ('王蓝','技术部',5000)
- ) AS EMPLOY(NAME,DEPT,SALARY);
- 查询结果如下:
- 姓名 工资 LAG0 LAG1 LAG2 LAG3 LAG4 LEAD
- 张三 2000 2000 (null) (null) 0 -1 2400
- 赵红 2400 2400 2000 (null) 0 -1 3000
- 李四 3000 3000 2400 2000 0 -1 3200
- 李白 3200 3200 3000 2400 2000 -1 4000
- 王五 4000 4000 3200 3000 2400 2000 5000
- 王蓝 5000 5000 4000 3200 3000 2400 (null)
我们先来看一下LAG 和 LEAD 函数的声明,如下:
LAG(表达式或字段, 偏移量, 默认值, IGNORE NULLS或RESPECT NULLS)
LAG是向下偏移,LEAD是想上偏移,大家看一下上面SQL的查询结果就一目了然了。
到此为止,有关DB2 OLAP 函数的所有知识都介绍给大家了,下面我们再次回顾一下 DB2 在线分析处理 的组成部分,如下:
函数 OVER(PARTITION BY 子句 ORDER BY 子句 ROWS或RANGE子句)
要想熟练掌握这些知识还需要一定的时间和练习,一旦你掌握了,你将拥有一项绝世武学,可以纵横DB2。
DB2 分组查询语句ROW_NUMBER() OVER() (转载)的更多相关文章
- oracle查询语句2【转载】
本文使用的实例表结构与表的数据如下: scott.emp员工表结构如下: SQL> DESC SCOTT.EMP; Name Type Nullable Defaul ...
- SQL Serever学习9——基础查询语句
SQL语言概述 SQL是结构化查询语言(Structure Query Language),1974年提出,1979年被IBM实现,SQL语言已经成为关系型数据库的标准语言. 包括: DDL数据定义语 ...
- spring data jpa条件分组查询及分页
原book对象 package com.shaying.domain; import javax.persistence.Column; import javax.persistence.Entity ...
- 在论坛中出现的比较难的sql问题:3(row_number函数 分组查询)
原文:在论坛中出现的比较难的sql问题:3(row_number函数 分组查询) 最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了. 所 ...
- 不同数据库oracle mysql SQL Server DB2 infomix sybase分页查询语句
在不同数据库中的使用的分页查询语句: 当前页:currentpage 页大小:pagesize 1. Oracle数据库 select * from (select A.*,rownum rn fro ...
- mysql系列九、mysql语句执行过程及运行原理(分组查询和关联查询原理)
一.背景介绍 了解一个sql语句的执行过程,了解一部分都做了什么,更有利于对sql进行优化,因为你知道它的每一个连接.where.分组.子查询是怎么运行的,都干了什么,才会知道怎么写是不合理的. 大致 ...
- SQL语句查询年龄分段分组查询
此情况用于数据库中没有“年龄”这个字段,只有“出生日期”这个字段.先计算出“年龄”,在分组查询. 1.SELECT *, ROUND(DATEDIFF(CURDATE(), popBirthday)/ ...
- 转载《mysql 一》:mysql的select查询语句内在逻辑执行顺序
原文:http://www.jellythink.com/archives/924 我的抱怨 我一个搞应用开发的,非要会数据库,这不是专门的数据库开发人员干的事么?话说,小公司也没有数 据库开发人员这 ...
- Oracle - 查询语句 - 分组函数
/* 分组函数 不能再select子句中出现普通的列,除非这个列在group by中给出 所有的空值都会被分为一组 分组过滤 SELECT FROM WHERE GROUPBY HAVING ORDE ...
随机推荐
- db2数据库字段更新当前时间
db2数据库中想要将字段的时间通过sql语句的方式更新: 例如: Update tablename set 字段1='打酱油', 字段2 = TO_CHAR(current timestamp,'YY ...
- “我放弃了年薪20W的offer......”
我的职业生涯开始和大多数测试人一样,开始接触都是纯功能界面测试.那时候在一家电商公司做测试,做了有一段时间,熟悉产品的业务流程以及熟练测试工作流程规范之后,效率提高了,工作比较轻松,也得到了更好的机会 ...
- Python Ethical Hacking - BACKDOORS(7)
Handling Errors: If the client or server crashes, the connection will be lost. Backdoor crashes if: ...
- javascript : 复杂数据结构拷贝实验
数组深拷贝看起来很简单. array.concat()就行了. 但是,如果数组里有对象呢? 实际上,你以为你拷贝了对象,但实际上你只拷贝了对象的引用(指针)! 我们可以做个试验. // test le ...
- 动手实现一个较为简单的MQTT服务端和客户端
项目地址:https://github.com/hnlyf168/DotNet.Framework 昨天晚上大致测试了下 ,490个客户端(一个收一个发) 平均估计每个每秒60个包 使用mqtt协 ...
- 详解Vue大护法——组件
1.什么是组件化 人面对复杂问题的处理方式: 任何一个人处理信息的逻辑能力都是有限的 所以,当面对一个非常复杂的问题时,我们不太可能一次性搞定一大堆的内容. 但是,我们人有一种天生的能力,就是将问题进 ...
- 毫无基础的人入门Python,Python入门教程
随着人工智能的发展,Python近两年也是大火,越来越多的人加入到Python学习大军,对于毫无基础的人该如何入门Python呢?这里整理了一些个人经验和Python入门教程供大家参考. 如果你是零基 ...
- 一个文本框的andriod教程
https://blog.csdn.net/androidmsky/article/details/49870823
- OFD呼之欲来、来之跚跚,谁之罪?
软件国产化的浪潮势不可挡,美国逼得逾甚,我们压迫感逾强,唯有奋起直追方慰平生之志. 在板式文档领域,pdf已成为国际标准,亦可称为美国标准:它在该领域一枝独秀,已形成一览众山小之势! pdf出道20余 ...
- JavaFX让UI更美观-CSS样式
相对于Swing来说,JavaFX在UI上改善了很多,不仅可以通过FXML来排版布局界面,同时也可以通过CSS样式表来美化UI. 其实在开发JavaFX应用的时候,可以将FXML看做是HTML,这样跟 ...