前面两篇咱们从 SQL 的最基础语法讲起,到表联结多表查询。 你们能够点击连接查看sql
今天咱们讲一些在作报表和复杂计算时很是实用的分析函数。因为各个数据库函数的实现不太同样,本文基于 Oracle 12c 。post
这个函数在平时用的仍是比较多的。这个函数的做用是为分组内的每一行返回一个行号。咱们仍是举例来讲明。 假设咱们有如下数据表:ui
共 8 个订单,分为 A,B,C,D四种类型,后面两列是订单描述和订单数量。spa
假如咱们如今想找到每一个订单类型中数量最少的一行记录,好比想找到 A 类型订单数量最少的,B 类型订单数量最少的。。。 咱们要怎么写呢 ? 用 GROUP BY 可能会很麻烦。这里用 ROW_NUMBER() 就很合适3d
SELECT order_no,
order_type,
order_text,
order_qty,
row_number() OVER(PARTITION BY order_type order by order_qty) AS rowno
FROM wip_order_test
复制代码
结果: code
能够看到,每一行最后都有一个从低到高的编号,有了这个编号咱们就能够经过取编号为 1 的行来获得每一个分组中订单数量最少的一行记录。cdn
解释一下,ROW_NUMBER() 为每一行返回一个行号, partition by 表示分组,这里表示根据 order_type 分组,而后咱们按照订单数量排序。就会获得每一个分组内的按照订单数量排序的行号。blog
假如咱们如今要 查询每一个类型的订单总数分别是多少,要怎么作? 你们可能会想到 GROUP BY,不过你们能够本身试试,是否能获得和我一样的结果
SELECT order_no,
order_type,
order_text,
order_qty,
sum(order_qty) OVER(PARTITION BY order_type) AS sum_qty
FROM wip_order_test
复制代码
结果:
看到后面多了一个数量列,就是每一个分组的订单总数量。是否是很方便?
除了 SUM 函数,其余几个计算函数如 AVG(),MAX(),MIN(),COUNT()的使用方法和 SUM 同样。
窗口函数能够对一个结果集内的必定范围内值进行累积,或者经过移动窗口进行累积。仍是看例子吧。
SELECT order_no,
order_type,
order_text,
order_qty,
sum(order_qty) OVER
(ORDER BY order_no ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS cumulative_qty
FROM wip_order_test;
复制代码
解释一下:仍是用 SUM 来计算总和,这里咱们使用了新的语法, ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 定义了窗口的起点和终点,UNBOUNDED PRECEDING表示起点在第一行,CURRENT ROW 表示终点在当前行。咱们看一下上图的结果,能看到最后一列的值是逐行累加的。
上面咱们的窗口的起点是固定的,终点逐渐往下移,咱们能够建立一个固定大小的窗口,起点和终点同时往下移动。只须要修改 UNBOUNDED 为一个固定的数字就能够了。咱们修改为 2, 和 3 分别看一下
SELECT order_no,
order_type,
order_text,
order_qty,
SUM(order_qty) OVER (ORDER BY order_no ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS cumulative_qty2,
SUM(order_qty) OVER (ORDER BY order_no ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS cumulative_qty3
FROM wip_order_test;
复制代码
解释下:倒数第二列咱们修改窗口起点2,表示当前行与前两行之间的范围。能够看到每一行的值都是当前行与它前面两行的值的累加。而最后一列,是当前行与它以前3行的值的累加。每处理一行,窗口的起点和终点都向下移动。
同理,SUM 也能够改成 AVG 求窗口的平均值
FIRST_VALUE() 和 LAST_VALUE()能够获取窗口的第一行和最后一行,NTH_VALUE()能够获取第 N 行。看一下例子:
SELECT order_no,
order_type,
order_text,
order_qty,
first_value(order_qty) OVER (ORDER BY order_no ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS first_value,
last_value(order_qty) OVER (ORDER BY order_no ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS last_value,
nth_value(order_qty,2) OVER (ORDER BY order_no ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS second_value
FROM wip_order_test;
复制代码
这个函数颇有用,有时候在 GROUP BY 之后,咱们想让分组内的某一列的几个值显示在一行上,好比:
SELECT
order_type,
listagg(to_char(order_text),'-') WITHIN GROUP (ORDER BY order_type) AS text
FROM wip_order_test
GROUP BY order_type
复制代码
结果:
看到,经过 LISTAGG ,把每一个分组中的订单描述字段链接起来。第一个参数表示要合并的字段名字,第二个参数表示分隔符。
Oracle 12c中新增了对 TOP-N的支持。
SELECT order_no,
order_type,
order_text,
order_qty
FROM wip_order_test
FETCH FIRST 3 ROWS ONLY;
复制代码
咱们用 FETCH FIRST 3 取出了前 3 行数据,这里也可使用 FETCH FIRST 20 PERCENT ROWS ONLY 用百分比来取出前 20% 的数据。
还可使用 OFFSET 关键字,来表示从第几行开始取,好比 OFFSET 5 ROWS FETCH NEXT 3 ROWS ONLY 就表示从第 5 行开始往下取 3 行。
能够算一组值的中位数,传入一个参数,好比传入0.5 表示 1/2 中位数,0.75 表示 3/4 中位数
SELECT order_type,
percentile_cont(0.5) WITHIN GROUP (
ORDER BY order_qty) AS A,
percentile_cont(0.75) WITHIN GROUP (
ORDER BY order_qty) AS b
FROM wip_order_test
GROUP BY order_type
复制代码
咱们根据订单类型分组后,分别算出每种订单类型数量的 1/2 中位数和 3/4中位数。