[url]转载于 Bob Lyle 谈 DB2 中的 OLAP 函数[/url]
排列函数
第一类引入到 DB2 中的 OLAP 函数是 排列(ranking)函数,它们是在 DB2 Version 6 中引入的。这些排列函数提供了定义一个集合(使用 PARTITION 子句),然后根据某种排序方式对这个集合内的元素进行排列的能力。例如,假设我们有一个雇员表,现在要对每个部门内的雇员薪水进行排列。要实现这一点,我们需要一个函数调用,这个函数调用可以完成以下工作:
将分区(集合)定义为各个部门
将集合内的排序方式定义为按薪水排序。
按照惯例,我们一般会将薪水高的排在前面,所以我们将指定一个对薪水的降序排序方式。下面的例子展示了这个查询的查询和输出。
select empnum, dept, salary,
rank() over (partition by dept
order by salary desc nulls last) as rank,
dense_rank() over (partition by dept order by salary desc nulls last)as denserank,
row_number() over (partition by dept order by salary desc nulls last)as rownumber
from emptab;
EMPNUM DEPT SALARY RANK DENSERANK ROWNUMBER
------ ---- ------ ---- --------- ---------
6 1 78000 1 1 1
2 1 75000 2 2 2
7 1 75000 2 2 3
11 1 53000 4 3 4
5 1 52000 5 4 5
1 1 50000 6 5 6
--------------------------------------------------
9 2 51000 1 1 1
4 2 - 2 2 2
展示排序的例子
首先,让我们看一下函数调用。注意,rank 函数本身没有参数。这是因为 rank 函数不对任何参数执行任何计算。相反,rank 函数只是着眼于行集合--以及每一行在集合中的位置--正如排序方式所定义的那样。那么,我们如何为这个函数定义集合和排序方式呢?两者都是用 OVER 子句定义的。在这个例子中,因为我们要在每个部门内进行排列,因此我们通过按部门划分分区来定义集合。这样做的效果是可以确保只有那些在 dept 列具有相等值的行才参与排列。对排列函数的而言, 分区(partition) 和 集合(set)这两个术语是等价的。在 PARTITION 子句后面,我们有一个 ORDER BY 子句,这个子句定义了分区内的排序方式。在这里,我们想将高薪排在前面,因此我们将排序方式定义为降序。除了指定降序以外,我们还指定 NULLS LAST。在 SQL 中,空值排在前面,意即空值显得要大于所有其他非空的值。这就给排列带来了问题,因为我们可能并不想将为空的薪水排在前面。因此,我们使用 NULLS LAST 子句来更改默认的排序方式,这样就可以将空值排在后面了。(注意,NULLS LAST 子句是在 DB2 V7 中引入的;不过,在 V6 中使用一个 CASE 表达式来强加排序方式也是可以的。)
现在,让我们看一下输出。前 6 行都是 Department 1 的雇员,每一行都被赋予一个按薪水降序排列所得的名次。注意,在 Department 1 中,有两个雇员的薪水都是 75000,这两行都被赋予第二的名次。这是因为 rank 函数提供了一种 “奥林匹克式”的排列方式,在这种方式中,两个相等的值得到相等的名次。因为有两行“结在一起,同获第二”,所以就没有排在第 3 的行。相反,接下来的一行排在第 4,因为根据排序方式,有 3 行严格地排在这一行之前。
对于 Department 2,注意其中有一个行具有为空的薪水。因为我们指定了 NULLS LAST,所以这一行被排在非空行的后面。如果我们没有指定 NULLS LAST 的话,Department 2 中两行的排列顺序就要倒过来了。
到现在,您可能会问自己,在上面的例子中,其他两个输出列 denserank 和 rownumber 是什么呢?DB2 实际上有三个不同的排列函数。首先是 rank 函数,它提供了奥林匹克式的排列方式,这在前面已经描述过了。其他两个函数分别是 dense_rank和 row_number。Dense_rank 很像 rank,在这个函数中,“结”中的行排名是相等的。这两个函数惟一的不同之处在于对跟在结后面的值的处理方式,在 Dense_rank函数中排名是按 1 递增的(而不是像 rank 函数那样按结中行的数量来递增)。因而,这里不会出现排名有间隔的现象(因此函数名中才用了“dense”)。虽然 Employee 11 的薪水在 rank 函数中获得的名次是第 4,但是 denserank 函数返回的值是 3。
最后一列给出 row_number 函数的输出。Row_number 也执行一次排列,但是当碰到有结的情况时,结中的行要进行任意的(也就是说,不是确定的)排序。这在对有重复值的数据进行分析时很有用。row_number 函数一个有趣的方面是它是惟一不要求提供排序方式的排列函数。如果在没有指定排序方式的情况下调用 row_number 函数,则所有的行都将被当作结中的行来对待,因而这些行是任意排序的。这对于在输出中给行排序来说很有用。
其他 OLAP函数
在 DB2 Version 7 中还引入了许多其他的 OLAP 函数。在引入这些函数之前,DB2 支持两类基本的函数,分别是 标量(scalar)函数和 聚集(aggregate) 函数。标量函数是那些对单个行中的值进行操作、并在每一行返回一个结果的函数。arithmetic 和 string 函数就是标量函数的例子。例如,下面的查询使用了 DIGITS 标量函数来格式化 salary 字段。该函数对每一行执行结果计算,并且该计算只使用当前行中的 salary 值。
select empnum, salary,
digits(salary) as digits
from emptab
where dept = 1;
EMPNUM SALARY DIGITS
----------- ----------- ----------
1 50000 0000050000
2 75000 0000075000
5 52000 0000052000
...
展示 DIGITS 标量函数的例子
聚集函数(也叫 列 或 集合 函数)的行为有所不同。聚集函数对一组行进行操作,并在输出中将这些行聚集(或者合并)到单个的行中。聚集函数的一个例子是 sum 函数,这个函数计算一组值的和,并将这个和放入一个结果行中。例如,下面的查询计算每个部门中所有雇员薪水的总和。GROUP BY 子句用于表明要聚集的集合(或分区)是各个部门中所有行的集合。对于每个部门都返回一行,给出该部门中所有薪水的总和。
select dept, sum(salary) as sum
from emptab
group by dept;
DEPT SUM
----------- -----------
1 383000
2 51000
3 209000
- 84000
展示 SUM 聚集函数的例子
在 DB2 V7 中引入的 OLAP 函数引入了一类新的函数,我们称之为 标量-聚集(scalar-aggregate) 函数。这些函数像标量函数,因为它们也是在每一行返回单个的值,但是它们也像聚集函数,因为它们要对一个集合中多个行中的值执行计算,以计算出结果。下面的标量-聚集函数执行的是与 sum 聚集函数一样的计算,但是这个函数返回的是没有合并行的结果:
select dept, salary,
sum(salary) over (partition by dept) as deptsum,
avg(salary) over (partition by dept) as avgsal,
count(*) over (partition by dept) as deptcount,
max(salary) over (partition by dept) as maxsal
from emptab;
DEPT SALARY DEPTSUM AVGSAL DEPTCOUNT MAXSAL
----- ------- -------- ------- --------- --------
1 50000 383000 63833 6 78000
1 75000 383000 63833 6 78000
1 52000 383000 63833 6 78000
1 78000 383000 63833 6 78000
1 75000 383000 63833 6 78000
1 53000 383000 63833 6 78000
2 - 51000 51000 2 51000
2 51000 51000 51000 2 51000
3 79000 209000 69666 3 79000
3 55000 209000 69666 3 79000
3 75000 209000 69666 3 79000
- - 84000 84000 2 84000
- 84000 84000 84000 2 84000
展示 SUM 报告函数的例子
注意,该查询没有包含 GROUP BY 子句。相反,该查询使用了 OVER 子句来对数据分区,以便 sum 函数对同一部门中的行执行计算,并在每一个部门内的每一行中返回该部门所有薪水的总和。按惯例,为了在每一行中包括那样的聚集结果,我们需要使用一个联合,但是现在 OLAP 函数为此提供了更简易的模式。我们推荐使用这种类型的函数作为 报告 函数,因为这种函数是对集合计算总和,并在每一行中都报告一次结果的。我曾经在前面和后面的例子中使用了 SUM, 但是大部分聚集函数(例如 AVG、MIN、MAX、STDEV,等等)都使用 OVER 子句。在 DEPTSUM 列右边的其他列显示了平均薪水、部门中雇员的人数以及部门中的最高薪水。惟一不支持作为标量-聚集函数的聚集函数是线性回归函数。
这些报告函数一个强大的用处就是计算比率和百分比。要计算某个雇员的薪水占整个部门薪水总和的百分比,只需简单地用报告的薪水总和去除该雇员的薪水。
select empnum, dept, salary,
sum(salary) over (partition by dept) as deptsum,
decimal(salary,10,2) /
sum(salary) over(partition by dept)as percentage
from emptab;
EMPNUM DEPT SALARY DEPTSUM PERCENTAGE
------ ----- -------- ----------- ----------
1 1 50000 383000 0.1305
2 1 75000 383000 0.1958
5 1 52000 383000 0.1357
6 1 78000 383000 0.2036
7 1 75000 383000 0.1958
11 1 53000 383000 0.1383
4 2 - 51000
9 2 51000 51000 1.0000
8 3 79000 209000 0.3779
10 3 55000 209000 0.2631
12 3 75000 209000 0.3588
0 - - 84000
3 - 84000 84000 1.0000
百分比的例子
用占总薪水支出的百分比表示的雇员薪水
如果我们在要进行聚集的集合中引入一个排序方式,会出现什么情况呢?答案是,我们不处理一个 报告( reporting ) 函数,而是处理一个 累加( cumulative )函数。累加函数是一种标量-聚集函数,它对当前行 以及集合中当前行之前(相对排序方式而言)的所有行进行操作。让我们为这个例子使用一个不同的表。假设我们有一个这样的表,它记有当前历年的每月销售业绩。那么,我们如何计算每个月的 年至今日(year-to-date) 销售数字呢?这里,我们要计算每月销售的累加和。我们可以这样做:
select date, sales,
sum(sales) over (order by date) as cume_sum,
count(*) over (order by date) as setcount
from sales
where year(date) = 2000;
DATE SALES CUME_SUM SETCOUNT
---------- ------------ ------------ ---------
01/01/2000 968871.12 968871.12 1
02/01/2000 80050.05 1048921.17 2
03/01/2000 757866.14 1806787.31 3
04/01/2000 58748.13 1865535.44 4
05/01/2000 40711.69 1906247.13 5
06/01/2000 241187.78 2147434.91 6
07/01/2000 954924.16 3102359.07 7
08/01/2000 502822.96 3605182.03 8
09/01/2000 97201.45 3702383.48 9
10/01/2000 853999.45 4556382.93 10
11/01/2000 358775.59 4915158.52 11
12/01/2000 437513.35 5352671.87 12
计算累加和的例子
每月销售量和到当前日期的累加销售量
让我们看一下结果。对于第一行,累加和就等于这一行的销售量。对于第二行,累加和等于一月份和二月份销售量的和(968871.12 + 80050.05 = 1048921.17)。类似地,第三行的结果是一月份、二月份和三月份销售量的和。在 CUME_SUM 列右边的列执行一个累加计数,给出在集合中行的数量。例如,第一行只有一行被求和(也就是该行本身),第二行有两行被求和(该行本身以及前一行),依此类推。上面的图给出了销售数字以及在前面的查询中计算出的累加和的图形化表示。
如果我们有多年的数据,并且想计算 每一年内 到当月的累加和,那么我们也可以像下面这样使用 PARTITION BY 子句:
select date, sales,
sum(sales) over (partition by year(date)
order by month(date)) as cume_sum
from sales
where year(date) >= 2000;
DATE SALES CUME_SUM
---------- ------------ -----------
01/01/2000 968871.12 968871.12
02/01/2000 80050.05 1048921.17
03/01/2000 757866.14 1806787.31
04/01/2000 58748.13 1865535.44
05/01/2000 40711.69 1906247.13
06/01/2000 241187.78 2147434.91
07/01/2000 954924.16 3102359.07
08/01/2000 502822.96 3605182.03
09/01/2000 97201.45 3702383.48
10/01/2000 853999.45 4556382.93
11/01/2000 358775.59 4915158.52
12/01/2000 437513.35 5352671.87
01/01/2001 476851.71 476851.71
02/01/2001 593768.12 1070619.83
03/01/2001 818597.97 1889217.80
...
使用 PARTITION BY 子句计算累加和
现在,请注意 2001年1月那一行是如何重置的。这是因为日期按年划分了分区,而在 2001年内 没有在一月份之前的行,因此 cume_sum 就等于一月份的销售量。这个例子还演示了另一件有趣的事情,那就是 OVER 子句使用的参数可以是表达式,而不仅仅是列值。在更复杂的例子中,甚至可能会将其他的聚集函数嵌入到标量-聚集函数调用中。这很有用,因为在执行分析之前先执行某种类型的聚集(例如,将销售量聚集到月的层次上)是十分常见的。这就引发了下面的问题:何时处理标量-聚集函数?答案是在处理选择清单中剩下的部分时处理这些函数。通常,一个查询的处理顺序是这样的:
From 子句
Where 子句
Group By 子句
Having 子句
选择清单
您可以看到,选择清单是在查询的所有其他部分处理完之后才被处理的。这意味着如果您有谓语(在 WHERE 或 HAVING 子句中),或者您有任何作为 GROUP BY 子句结果的聚集,那么在处理标量-聚集函数之前首先要应用这些东西。例如,让我们看下面的查询:
select year(date) as year, sum(sales) as sum,
sum(sum(sales)) over (order by year(date)) as cume_sum
from sales
where year(date) >= 1995
group by year(date);
YEAR SUM CUME_SUM
----------- ------------- ------------
1995 7731162.39 7731162.39
1996 4127017.98 11858180.37
1997 7211584.76 19069765.13
1998 4149296.50 23219061.63
1999 6278023.54 29497085.17
2000 5352671.87 34849757.04
2001 5736777.81 40586534.85
对一个聚集的累加和
在这个例子中,我们访问表(在 FROM 子句中指定)并应用 WHERE 子句,然后应用 GROUP BY 子句并计算每年的销售总量。最后,我们处理选择清单,包括所有的标量-聚集函数。
这里还要讲一点。因为标量-聚集函数是在 WHERE 子句 之后处理的,因此在一个谓语中引用标量-聚集函数是不可能的。相反,如果您想这么做,您就必须 嵌套 标量-聚集函数调用,要么是嵌套在一个公共表表达式内,要么是嵌套在一个嵌套查询内。这在执行返回前 n 行结果的查询时变得很有用。一个这样例子就是编写一个用于选择具有最高销售总量的3年的查询。我们可以通过对每年的销售量排列、然后选择名次为 3 或者更小的行这种方法来做这件事。
with ranked_years (year, sum, rank) as
(select year(date) as year, sum(sales) as sum,
rank() over (order by sum(sales) desc) as rank
from sales
group by year(date)
)
select year, sum, rank
from ranked_years
where rank <= 3;
YEAR SUM RANK
----------- ------------- -------
1995 7731162.39 1
1997 7211584.76 2
1999 6278023.54 3
Top n查询的例子
在这个例子中您可以看到,我们有一个公共表表达式,它执行聚集来计算每年的销售总量,然后对销售总量进行排列。接着,外围的选择使用这个结果表并添加一个谓语,使得查询只返回那些名次 <=3(也就是销售总量排在前3名)的行。要计算中数、百分位以及其他分布统计,也可以执行类似的查询。
我希望至此我已经传达了这些新 OLAP 函数是什么以及如何使用它们的大致信息。实际上,有关这些函数的内容比我在这里描述的要多得多。因此,敬请关注另一篇更详细介绍这些函数的文章。
我想告诉你的是,DB2 已经使用这些 OLAP 函数的实现扩展了关系模型,因而现在关系模型就可以理解相对于数据集合的 排序方式。如果您曾经试过编制牵涉到排序方式的查询,您就知道这些查询可以变得多么的困难和复杂(即使是像中数这样的简单查询也是如此)。OLAP 函数为您提供了可以高效、简明地编制那样的查询的工具。随着对 DBMS 的需求的日益增长,显然必须将传统的关系模型加以扩展,以便能够处理那些越来越复杂的分析,而这些函数正是 DB2 打破局限的一个例子。
分享到:
相关推荐
### DB2中的OLAP函数详解 #### 一、引言 在数据库管理领域,特别是针对IBM的DB2 Universal Database,OLAP(Online Analytical Processing,在线分析处理)函数的引入极大地提高了数据分析的能力和效率。本文将...
Web安全学习笔记
要获得演讲中所有幻灯片的概述,请按ESC。 目录 后续材料 该研讨会将为您首次介绍Scala和函数式编程范例。 如果您想了解更多细节,您可能会对提供的免费书籍感兴趣。 准备工作 强制性的 在开始之前,您必须安装以
在实际使用时,开发者需要将这个库解压并将其源代码导入到自己的Bash脚本项目中,然后就可以调用库中的函数了。通常,库会有一个简短的使用指南或示例脚本,说明如何导入和使用这些功能。 总之,`bash-utility`为...
《CUDA与CPU在PSO优化算法中的性能对比——基于3维Levy函数的实验》 在计算机科学领域,特别是优化算法的研究中,粒子群优化(Particle Swarm Optimization, PSO)是一种受到生物群体行为启发的全局搜索算法。它...
在本项目"ExemplosCsharp"中,我们聚焦于C#编程语言,特别是针对.NET框架的学习者。项目提供了一系列的C#函数示例,帮助初学者和有经验的开发者更好地理解和应用C#语言特性。这些示例不仅涵盖基础概念,还涉及到更...
师生互动作为课堂位置的函数 心理社会评估 119 BLOOM, It. D., & WILENSKY H. 描述教师行为的四个观察类别。 BREYER, NL, & COLCHEN, DJ 学生和教师的行为观察计划。 Storrs, CAPLAN, G. 心理健康咨询的理论...
args2object Node/JavaScript 库,用于访问给定函数参数作为路径的对象结构中的项目。安装在节点$ npm install args2object手动安装浏览器应用程序获取dist/args2object.bundle.js文件并将其包含在您的 HTML 中: &...
使用Python函数从USGS EarthExplorer交付系统下载的GeoTIFF文件中读取SRTM高程。 USGS EarthExplorer是USGS首选的下载SRTM数据文件的方法。 以前,SRTM磁贴为.hgt格式,EarthExplorer传送/下载仅允许BIL,DTED或Geo...
贝叶斯推理随机过程是一门应用贝叶斯方法处理随机场模型中的不确定性的学问,它是统计学的一个重要分支。随机过程是随机变量随时间变化的集合,它描述了随机现象随时间演化的复杂动态。在随机过程中运用贝叶斯推理...
该项目是Lyle Franklin出色的Concourse地形资源的包装。 它旨在简化跨Github Actions和Concourse Pipelines的可移植性。 输入项 env_name 可选要管理的环境的名称,例如暂存。 将使用该名称创建Terraform工作空间。...
在字符串中查找日期使用正则表达式在字符串中查找日期的函数用法$string = "This building was cleaned on the 8th of October 2006 after a huge storm."$date = find_date( $string );// Return:array('day'=> 08,...
Lyle接受了这个建议,最终在1984年的超级碗比赛中,他们赢得了世界冠军,Lyle也表现得极为出色,证明了自己。 这段内容中的知识点包括: 1. **教练角色**:教练不仅需要评估球员的技术水平,还要关注球员的心理...
在Markdown,Jupyter或RStudio中(通过Blogdown)编辑您的网站,使用Hugo生成它,并使用GitHub或Netlify进行部署。 使用窗口小部件,主题和语言包自定义网站上的任何内容。 :backhand_index_pointing_right: :...
4. 英语教学的理论与实践:文件中包含的信息指向了英语教师不仅需要有扎实的理论知识,还需要掌握将理论应用于实际教学中的实践技能。教师们需要对语言教学的各个方面有深刻理解,并能够有效地将这些知识传授给学生...
1. **Tate & Lyle**:作为全球领先的食品配料供应商之一,Tate & Lyle 在聚葡萄糖生产领域占据着重要的市场份额。 2. **Danisco**:丹尼斯克公司是一家丹麦跨国公司,专注于食品配料的研发和生产,其在聚葡萄糖市场...
在全球市场中,羟丙基淀粉磷酸酯的需求逐渐增长,主要得益于其在食品工业中的广泛应用。它作为食品添加剂,可以改善食品的口感、保水性、稠度和冷冻解冻稳定性,尤其在烘焙、乳制品、糖果和冷冻食品等领域扮演重要...
JavaScript 函数库、工具类 Browser Support 7+ :check_mark: Latest :check_mark: Latest :check_mark: Latest :check_mark: Latest :check_mark: 6+ :check_mark: Docs 查看文档 Installing npm install xe-utils...