【分析函数】使用分析函数LAST_VALUE或11g LAG实现缺失数据填充及其区别
2014-06-13 20:24
441 查看
转载自:http://blog.chinaunix.net/uid-7655508-id-3736949.html
在“使用Partitioned
Outer Join实现稠化报表”这篇文章中,讲述了实现稠化数据填充的方法。这篇文章和上述文章有所不同,主要讲述实现对指定的空行,按照前面非空或后面非空数据进行填充。原来这种实现数据填充的方法,主要是用LAST_VALUE+IGNORE NULLS(10g)实现,在11G中LAG分析函数也支持IGNORE
NULLS,但是,在性能上,他们是有区别的。
本文讨论2点内容:
1.使用分析函数LAST_VALUE和11G
LAG实现缺失数据填充。
2.LAST_VALUE和LAG在实现缺失数据填充上的区别。
1.使用分析函数LAST_VALUE和11G LAG实现缺失数据填充。
经常我们在报表中遇到这样的问题:
例1:对每行VAL为空的,向上找最近的不为空的VAL,然后填充到当前为止
在10g中有LAST_VALUE+IGNORE NULLS很好解决,如下:
上面的SQL含义是ID排序直到当前行(默认是RANGE窗口),忽略VAL为空的值,因为是LAST_VALUE,所以找最近的不为空的VAL值来填充到当前行。在11G中,LAG分析函数也带IGNORE NULLS,所以也能实现上面的功能,因为LAG是找当前行前面1行的值,所以需要加个NVL,LAST_VALUE不需要,它是直接找到当前行,否则有值的可能为空,如下:
当然,具体需求总是复杂的,如果变换一下:
例2:如果前面找不到值填充(也就是前面的全是NULL),那么就向后查找最近的一条不为空的值填充。如下:
对于ID=1和ID=2的行,因为前面找不到VAL的值,所以用ID=3的来填充。很显然,这里需要用到2次LAST_VALUE分析函数,一次是正常用当前行前面的VAL来填充,如果填充不了,就用按ID倒叙排列的最近一行来填充。如下:
黄色区域的数据还是按向上查找的填充方式,红色部分按照向下查找填充的方式。当然,也可以使用LAG或LEAD来实现:如下:
有没有注意到,使用LAG,排序是ORDER BY ID DESC,使用LEAD,则排序是ORDER BY ID。因为LAG默认是找按照ID排序,找当前行之前的1行,LEAD则是找当前行之后的1行(都是忽略NULL后的结果)对应的值,所以它们这样写是等价的。但是为什么使用LAST_VALUE的时候,我没有用FIRST_VALUE+ORDER
BY ID呢,显然这是不行的, LAST_VALUE是按照排序,直到找到当前行,返回最大的ID对应的值(忽略NULL),它计算的不是当前行之前的1行值,FIRST_VALUE是按照排序,找对应窗口的最小ID对应的值(忽略NULL)。所以LAST_VALUE+ORDER BY ID DESC不等价于FIRST_VALUE+ORDER BY ID。见下表格,表示两者之间的区别:
LAG/LEAD
LAST_VALUE/FIRST_VALUE
例3:继续变换下需求,如果按照CATE分区域,每个区域内按照先从上面查找,找到则用最近的VAL填充,否则向下查找,用最近的VAL填充。如下:
上面的ID=6的按照前面的方法,用ID=3的填充,但是现在要按CATE分区,所以应该用ID=7的填充,则前面分析函数要加上PARTITION BY子句:
2.LAST_VALUE和LAG在实现缺失数据填充上的区别。
LAST_VALUE分析可以可以带WINDOW子句,而LAG分析函数不可以,这意味着,LAST_VALUE分析函数更强大,通过前面的例子可以看出,LAST_VALUE实现一般的缺失数据填充,不需要NVL的,而LAG还需要NVL,因为它们的含义是完全不同的。比如要实现从之前开始找,再向后找至多2行,然后用最大的ID对缺失数据填充。如果使用LAST_VALUE,因为现在不是找到当前行的最后一个ID对应的值了,所以,必须加NVL,否则有值也会被转掉:
如果上面的需求使用LAG分析函数来实现,那就比较复杂了。
另外LAG/LEAD分析函数带IGNORE NULLS是11G新特性,它的效率远远比LAST_VALUE要差:
先构造9999行数据如下:
测试缺失数据填充,为公平起见,LAST_VALUE也加上NVL:
统计信息完全一样,但是LAST_VALUE是0.13s,LAG是22.49s,效率差别太大。经过10046跟踪,发现LAG分析函数+IGNORE NULLS大量消耗CPU,FETCH阶段消耗大量CPU TIME。如下:
看来LAG/LEAD的IGNORE NULLS内部实现比较差,效率远远不如LAST_VALUE的IGNORE NULLS内部实现,当然不加IGNORE NULLS的LAG/LEAD效率还是不错的,对于ORACLE新特性,一定要做足测试,慎用,期待12C能够优化LAG/LEAD IGNORE NULLS。
在“使用Partitioned
Outer Join实现稠化报表”这篇文章中,讲述了实现稠化数据填充的方法。这篇文章和上述文章有所不同,主要讲述实现对指定的空行,按照前面非空或后面非空数据进行填充。原来这种实现数据填充的方法,主要是用LAST_VALUE+IGNORE NULLS(10g)实现,在11G中LAG分析函数也支持IGNORE
NULLS,但是,在性能上,他们是有区别的。
本文讨论2点内容:
1.使用分析函数LAST_VALUE和11G
LAG实现缺失数据填充。
2.LAST_VALUE和LAG在实现缺失数据填充上的区别。
1.使用分析函数LAST_VALUE和11G LAG实现缺失数据填充。
经常我们在报表中遇到这样的问题:
例1:对每行VAL为空的,向上找最近的不为空的VAL,然后填充到当前为止
dingjun123@ORADB> SELECT * FROM t; ID VAL CATE ---------- ---------- ---------- 1 VAL1 CATE0 2 CATE0 3 CATE0 4 CATE0 5 CATE0 6 VAL6 CATE1 7 CATE1 8 CATE1 9 CATE1 9 rows selected. |
dingjun123@ORADB> SELECT ID, 2 last_value(val IGNORE NULLS) over(ORDER BY ID) val, 3 cate 4 FROM t; ID VAL CATE ---------- ---------- ---------- 1 VAL1 CATE0 2 VAL1 CATE0 3 VAL1 CATE0 4 VAL1 CATE0 5 VAL1 CATE0 6 VAL6 CATE1 7 VAL6 CATE1 8 VAL6 CATE1 9 VAL6 CATE1 9 rows selected. |
dingjun123@ORADB> SELECT ID, 2 nvl(val,lag(val IGNORE NULLS) over(ORDER BY ID)) val, 3 cate 4 FROM t; ID VAL CATE ---------- ---------- ---------- 1 VAL1 CATE0 2 VAL1 CATE0 3 VAL1 CATE0 4 VAL1 CATE0 5 VAL1 CATE0 6 VAL6 CATE1 7 VAL6 CATE1 8 VAL6 CATE1 9 VAL6 CATE1 9 rows selected. |
例2:如果前面找不到值填充(也就是前面的全是NULL),那么就向后查找最近的一条不为空的值填充。如下:
dingjun123@ORADB> select id,val,cate from t; ID VAL CATE ---------- ---------- ---------- 1 CATE0 2 CATE0 3 VAL3 CATE0 4 CATE0 5 CATE0 6 VAL6 CATE1 7 CATE1 8 CATE1 9 CATE1 9 rows selected. |
dingjun123@ORADB> SELECT ID, 2 nvl(last_value(val IGNORE NULLS) over(ORDER BY ID), 3 last_value(val IGNORE NULLS) over(ORDER BY ID DESC)) val, 4 cate 5 FROM t 6 ORDER BY ID; ID VAL CATE ---------- ---------- ---------- 1 VAL3 CATE0 2 VAL3 CATE0 3 VAL3 CATE0 4 VAL3 CATE0 5 VAL3 CATE0 6 VAL6 CATE1 7 VAL6 CATE1 8 VAL6 CATE1 9 VAL6 CATE1 9 rows selected. |
dingjun123@ORADB> SELECT ID, 2 nvl(val,nvl(lag(val IGNORE NULLS) over(ORDER BY ID),lag(val IGNORE NULLS) over(ORDER BY ID DESC))) val, 3 cate 4 FROM t 5 ORDER BY ID; ID VAL CATE ---------- ---------- ---------- 1 VAL3 CATE0 2 VAL3 CATE0 3 VAL3 CATE0 4 VAL3 CATE0 5 VAL3 CATE0 6 VAL6 CATE1 7 VAL6 CATE1 8 VAL6 CATE1 9 VAL6 CATE1 9 rows selected. dingjun123@ORADB> SELECT ID, 2 nvl(val,nvl(lag(val IGNORE NULLS) over(ORDER BY ID),lead(val IGNORE NULLS) over(ORDER BY ID))) val, 3 cate 4 FROM t 5 ORDER BY ID; ID VAL CATE ---------- ---------- ---------- 1 VAL3 CATE0 2 VAL3 CATE0 3 VAL3 CATE0 4 VAL3 CATE0 5 VAL3 CATE0 6 VAL6 CATE1 7 VAL6 CATE1 8 VAL6 CATE1 9 VAL6 CATE1 9 rows selected. |
BY ID呢,显然这是不行的, LAST_VALUE是按照排序,直到找到当前行,返回最大的ID对应的值(忽略NULL),它计算的不是当前行之前的1行值,FIRST_VALUE是按照排序,找对应窗口的最小ID对应的值(忽略NULL)。所以LAST_VALUE+ORDER BY ID DESC不等价于FIRST_VALUE+ORDER BY ID。见下表格,表示两者之间的区别:
LAG/LEAD
ID | VAL | CATE | 说明 |
1 | CATE0 | 使用LAG+ORDER BY ID DESC(忽略NULL,并且是找当前行之前的最近不为NULL的行), 则可以找到ID=3对应的行, 使用LEAD+ORDER BY ID,也可以找到ID=3的行(忽略NULL,找当前行之后最近不为NULL的行) | |
2 | CATE0 | ||
3 | VAL3 | CATE0 | |
4 | CATE0 | ||
5 | CATE0 | ||
6 | VAL6 | CATE1 | |
7 | CATE1 | ||
8 | CATE1 | ||
9 | CATE1 |
ID | VAL | CATE | 说明 |
1 | CATE0 | 使用LAST_VALUE+ORDER BY ID DESC(忽略NULL,找离当前行最近的ID对应的VAL值,包括当前行,正确), 则可以找到ID=3对应的行, 使用FIRST_VALUE+ORDER BY ID,(忽略NULL,找直到当前行的,因为ID=1的前面没有,所以必然还是NULL,ID=2的类似,当然可以+WINDOW窗口搞定) | |
2 | CATE0 | ||
3 | VAL3 | CATE0 | |
4 | CATE0 | ||
5 | CATE0 | ||
6 | VAL6 | CATE1 | |
7 | CATE1 | ||
8 | CATE1 | ||
9 | CATE1 |
--不加WINDOW窗口,不正确 dingjun123@ORADB> SELECT ID, 2 nvl(last_value(val IGNORE NULLS) over(ORDER BY ID), 3 first_value(val IGNORE NULLS) over(ORDER BY ID)) val, 4 cate 5 FROM t 6 ORDER BY ID; ID VAL CATE ---------- ---------- ---------- 1 CATE0 2 CATE0 3 VAL3 CATE0 4 VAL3 CATE0 5 VAL3 CATE0 6 VAL6 CATE1 7 VAL6 CATE1 8 VAL6 CATE1 9 VAL6 CATE1 9 rows selected. --加WINDOW窗口的FIRST_VALUE,正确 dingjun123@ORADB> SELECT ID, 2 nvl(last_value(val IGNORE NULLS) over(ORDER BY ID), 3 first_value(val IGNORE NULLS) over(ORDER BY ID ROWS BETWEEN unbounded preceding AND unbounded following)) val, 4 cate 5 FROM t 6 ORDER BY ID; ID VAL CATE ---------- ---------- ---------- 1 VAL3 CATE0 2 VAL3 CATE0 3 VAL3 CATE0 4 VAL3 CATE0 5 VAL3 CATE0 6 VAL6 CATE1 7 VAL6 CATE1 8 VAL6 CATE1 9 VAL6 CATE1 9 rows selected. |
dingjun123@ORADB> select id,val,cate from t; ID VAL CATE ---------- ---------- ---------- 1 CATE0 2 CATE0 3 VAL3 CATE0 4 CATE0 5 CATE0 6 CATE1 7 VAL7 CATE1 8 CATE1 9 CATE1 9 rows selected. |
dingjun123@ORADB> SELECT ID, 2 nvl(last_value(val IGNORE NULLS) over(PARTITION BY cate ORDER BY ID), 3 last_value(val IGNORE NULLS) over( PARTITION BY cate ORDER BY ID DESC)) val, 4 cate 5 FROM t 6 ORDER BY ID; ID VAL CATE ---------- ---------- ---------- 1 VAL3 CATE0 2 VAL3 CATE0 3 VAL3 CATE0 4 VAL3 CATE0 5 VAL3 CATE0 6 VAL7 CATE1 7 VAL7 CATE1 8 VAL7 CATE1 9 VAL7 CATE1 9 rows selected. SELECT ID, nvl(val,nvl(lag(val IGNORE NULLS) over(PARTITION BY cate ORDER BY ID), lead(val IGNORE NULLS) over(PARTITION BY cate ORDER BY ID))) val, cate FROM t ORDER BY ID; --结果一样,省略 |
LAST_VALUE分析可以可以带WINDOW子句,而LAG分析函数不可以,这意味着,LAST_VALUE分析函数更强大,通过前面的例子可以看出,LAST_VALUE实现一般的缺失数据填充,不需要NVL的,而LAG还需要NVL,因为它们的含义是完全不同的。比如要实现从之前开始找,再向后找至多2行,然后用最大的ID对缺失数据填充。如果使用LAST_VALUE,因为现在不是找到当前行的最后一个ID对应的值了,所以,必须加NVL,否则有值也会被转掉:
dingjun123@ORADB> SELECT ID,val, 2 nvl(val,last_value(val IGNORE NULLS) over(ORDER BY ID ROWS BETWEEN unbounded preceding AND 2 following)) new_val, 3 cate 4 FROM t; ID VAL NEW_VAL CATE ---------- ---------- ------------------------------------------- ---------- 1 VAL1 VAL1 CATE0 2 VAL1 CATE0 3 VAL1 CATE0 4 VAL6 CATE0 5 VAL6 CATE0 6 VAL6 VAL6 CATE1 7 VAL6 CATE1 8 VAL6 CATE1 9 VAL6 CATE1 9 rows selected. |
另外LAG/LEAD分析函数带IGNORE NULLS是11G新特性,它的效率远远比LAST_VALUE要差:
先构造9999行数据如下:
dingjun123@ORADB> DROP TABLE t; Table dropped. dingjun123@ORADB> CREATE TABLE t AS SELECT LEVEL ID,decode(MOD(LEVEL,5),1,'VAL'||LEVEL) val, 2 'CATE'||(trunc((LEVEL-1)/5)) cate FROM dual CONNECT BY LEVEL<10000; Table created. dingjun123@ORADB> select count(*) cnt,count(val) cnt_val from t; CNT CNT_VAL ---------- ---------- 9999 2000 1 row selected. |
dingjun123@ORADB> SELECT ID, 2 nvl(val,last_value(val IGNORE NULLS) over(ORDER BY ID)) val, 3 cate 4 FROM t; 9999 rows selected. Elapsed: 00:00:00.13 Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 31 consistent gets 0 physical reads 0 redo size 207607 bytes sent via SQL*Net to client 7741 bytes received via SQL*Net from client 668 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 9999 rows processed dingjun123@ORADB> SELECT ID, 2 nvl(val,lag(val IGNORE NULLS) over(ORDER BY ID)) val, 3 cate 4 FROM t; 9999 rows selected. Elapsed: 00:00:22.49 Statistics -------------------------------------------------------- 0 recursive calls 0 db block gets 31 consistent gets 0 physical reads 0 redo size 207607 bytes sent via SQL*Net to client 7741 bytes received via SQL*Net from client 668 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 9999 rows processed |
SELECT ID, nvl(val,lag(val IGNORE NULLS) over(ORDER BY ID)) val, cate FROM t call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.02 0 1 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 668 21.98 22.08 0 31 0 9999 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 670 21.98 22.11 0 32 0 9999 |
相关文章推荐
- Hive分析窗体函数之LAG,LEAD,FIRST_VALUE和LAST_VALUE
- Oracle 11g使用Pivot函数实现数据聚合行转列
- Hive分析函数之LAG、LEAD、FIRST_VALUE、LAST_VALUE学习
- 使用Oracle 11g函数Pivot实现数据聚合行转列
- Hive分析窗口函数之LAG,LEAD,FIRST_VALUE和LAST_VALUE
- Oracle分析函数总结(2) - 排序 - rank,dense_rank,row_number,first,first_value,last,last_value,lag,lead
- 分析函数--FIRST_VALUE,LAST_VALUE,LAG,LEAD,ROW_NUMBER
- 分析函数first_value()与last_value()
- 取上下行数据分析函数。lag()和lead() ——分析函数4
- 使用MS ACCESS + MS EXCEL实现最简单的数据分析。
- Oracle分析函数RANK(),ROW_NUMBER(),LAG()等的使用方法
- 使用SQL字符串反转函数REVERSE巧妙实现lastindexof功能
- 使用C#实现将XML数据填充到TreeView中
- JS中confirm,alert,prompt函数使用区别分析
- 用最简单的函数实现功能:判断一个int数据是否是2的x次幂(不能使用循环)。
- 利用Oracle分析函数row_number和sys_connect_by_path实现多行数据合并为一行
- [DB][ORACLE][统计函数]使用LAG和LEAD函数可以在一次查询中取出同一字段的前N行的数据和后N行的值
- 使用js函数实现的通过输入框中数据的长度来控制光标聚焦位置
- Oracle分析函数RANK(),ROW_NUMBER(),LAG()等的使用方法
- JS中confirm,alert,prompt函数使用区别分析