窗口函数_LAG()与LEAD() 详解
轉(zhuǎn)載自:Hive 窗口函數(shù)lead 和 lag(39)
補(bǔ)充:HIVE_HIVE函數(shù)_窗口函數(shù)_LAG()/LEAD() 詳解
lead 和 lag
前面我們學(xué)習(xí)的first_value和last_value 取的是排序后的數(shù)據(jù)截止當(dāng)前行的第一行數(shù)據(jù)和最后一行數(shù)據(jù)
Lag和Lead分析函數(shù)可以在一次查詢(xún)中取出當(dāng)前行后N行和前N行的數(shù)據(jù),雖然可以不用排序,但是往往只有在排序的場(chǎng)景下取前面或者后面N 行數(shù)據(jù)才有意義
這種操作可以代替表的自聯(lián)接,并且LAG和LEAD有更高的效率。
Lag/Lead(col,n,DEFAULT) 用于統(tǒng)計(jì)窗口內(nèi)當(dāng)前行往前或者往后第n行值
- 第一個(gè)參數(shù)為列名,
- 第二個(gè)參數(shù)為往后/前第n行(可選,默認(rèn)為1),
- 第三個(gè)參數(shù)為默認(rèn)值(當(dāng)往上第n行為NULL時(shí)候,取默認(rèn)值,如不指定,則為NULL)
需要注意的是lag 取得是當(dāng)前行之前的數(shù)據(jù),lead 取的實(shí)當(dāng)前行之后的數(shù)據(jù)
測(cè)試數(shù)據(jù)
我們有這樣一個(gè)文本user_access_log.txt,里面記錄了用戶(hù)對(duì)頁(yè)面的訪問(wèn)情況id,ctime,url 分別代表了用戶(hù)ID,訪問(wèn)時(shí)間和訪問(wèn)的頁(yè)面的URL
Peter 2015-10-12?01:10:00 url1 Peter 2015-10-12?01:15:10 url2 Peter 2015-10-12?01:16:40 url3 Peter 2015-10-12?02:13:00 url4 Peter 2015-10-12?03:14:30 url5 Marry 2015-11-12?01:10:00 url1 Marry 2015-11-12?01:15:10 url2 Marry 2015-11-12?01:16:40 url3 Marry 2015-11-12?02:13:00 url4 Marry 2015-11-12?03:14:30 url5數(shù)據(jù)說(shuō)明:Peter 2015-10-12 01:10:00 url1 ? ,表示Peter在2015-10-12 01:10:00進(jìn)入了網(wǎng)頁(yè)url1,即記錄的是進(jìn)入網(wǎng)頁(yè)的時(shí)間。
從例子中學(xué)習(xí)lead 和 lag
計(jì)算當(dāng)前用戶(hù)的第一次訪問(wèn)時(shí)間以及當(dāng)前訪問(wèn)時(shí)間的上一次訪問(wèn)時(shí)間和下一次訪問(wèn)時(shí)間
從描述我們知道了分區(qū)的條件或者子窗口的定義條件是用戶(hù)本身,從我們前面的學(xué)習(xí)中我們知道了第一次訪問(wèn)時(shí)間我們可以使用first_value 計(jì)算,下面我們看一下這個(gè)需求如何實(shí)現(xiàn)
selectuserid,url,ctime,first_value(ctime) over(partition by userid order by ctime) as first_ctime,lag(ctime,1) over(partition by userid order by ctime) as lag_ctime,lead(ctime,1) over(partition by userid order by ctime) as lead_ctime from ods_user_log;這里我們也對(duì)這個(gè)結(jié)果簡(jiǎn)單分析一下,對(duì)于第一行數(shù)據(jù)它是沒(méi)有l(wèi)ag_ctime的,也就是沒(méi)有上一次訪問(wèn)時(shí)間,因?yàn)樗堑谝淮卧L問(wèn),對(duì)于lead_ctime也就是下一次訪問(wèn)時(shí)間2015-11-12 01:15:10
計(jì)算用戶(hù)在每個(gè)頁(yè)面上的停留時(shí)長(zhǎng)
用戶(hù)Peter在瀏覽網(wǎng)頁(yè),在某個(gè)時(shí)刻,Peter點(diǎn)進(jìn)了某個(gè)頁(yè)面,過(guò)一段時(shí)間后,Peter又進(jìn)入了另外一個(gè)頁(yè)面,如此反復(fù),那怎么去統(tǒng)計(jì)Peter在某個(gè)特定網(wǎng)頁(yè)的停留時(shí)間呢,又或是怎么統(tǒng)計(jì)某個(gè)網(wǎng)頁(yè)用戶(hù)停留的總時(shí)間呢?
要計(jì)算Peter在頁(yè)面url1停留的時(shí)間,需要用進(jìn)入頁(yè)面url2的時(shí)間,減去進(jìn)入url1的時(shí)間,即2015-10-12 01:15:10這個(gè)時(shí)間既是離開(kāi)頁(yè)面url1的時(shí)間,也是開(kāi)始進(jìn)入頁(yè)面url2的時(shí)間。所以我們需要先獲取用戶(hù)在某個(gè)頁(yè)面停留的起始與結(jié)束時(shí)間
selectuserid,url,ctime as startTime, lead(ctime,1) over(partition by userid order by ctime) as leaveTime from ods_user_log;有了進(jìn)入時(shí)間和離開(kāi)時(shí)間我們就很容易計(jì)算出用戶(hù)在特定頁(yè)面上的停留時(shí)間,只需要用離開(kāi)時(shí)間減去進(jìn)入時(shí)間即可
selectuserid,url,ctime as startTime,lead(ctime,1) over(partition by userid order by ctime) as leaveTime,unix_timestamp(lead(ctime,1) over(partition by userid order by ctime) ) -unix_timestamp(ctime) as stayTime from ods_user_log;大家考慮一個(gè)問(wèn)題如果沒(méi)有l(wèi)ag和lead這樣的函數(shù),我們?cè)撊绾螌?shí)現(xiàn)這個(gè)需求呢,我們可以借助自關(guān)聯(lián)來(lái)實(shí)現(xiàn),但是這個(gè)自關(guān)聯(lián)就有很多種是想方式了,這里我借助row_number 來(lái)實(shí)現(xiàn)一種
with a as (select userid,url,ctime as startTime,row_number() over (partition by userid order by ctime ) as rnfromods_user_log ) select a1.userid userid,a1.startTime startTime,a2.startTime leaveTime,unix_timestamp(a2.startTime )-unix_timestamp(a1.startTime ) as stayTimefrom a a1 inner join a a2 on a1.userid=a2.useridand (a2.rn-a1.rn=1) ;這里我們主要是使用 (a2.rn-a1.rn=1) 的條件找出了用戶(hù)進(jìn)入頁(yè)面和離開(kāi)頁(yè)面的時(shí)間
使用場(chǎng)景
lead 和 lag 的使用場(chǎng)景其實(shí)是比較多的,尤其我們?cè)谟?jì)算需要自關(guān)聯(lián)的這種場(chǎng)景下,如果合理使用頭是可以很好的提高我們程序的性能和簡(jiǎn)化我們的寫(xiě)法,下面我們?cè)倏匆粋€(gè)例子
有個(gè)日志表中記錄了商戶(hù)費(fèi)率變化狀態(tài)的所有信息用戶(hù)ID,費(fèi)率,時(shí)間,現(xiàn)在有個(gè)需求,要取出按照時(shí)間軸順序,發(fā)生了費(fèi)率變化的數(shù)據(jù)行,輸出數(shù)據(jù)格式商戶(hù)ID,當(dāng)前時(shí)間,變化時(shí)間,原始費(fèi)率,變化后的費(fèi)率
100,0.1,2016-03-02 100,0.1,2016-02-02 100,0.2,2016-03-05 100,0.2,2016-03-06 100,0.3,2016-03-07 100,0.1,2016-03-09 100,0.1,2016-03-10 100,0.1,2016-03-10 200,0.1,2016-03-10 200,0.1,2016-02-02 200,0.2,2016-03-05 200,0.2,2016-03-06 200,0.3,2016-03-07 200,0.1,2016-03-09 200,0.1,2016-03-10 200,0.1,2016-03-10現(xiàn)在我們開(kāi)始建表
create table ods_user_rate_log(userid string,rate double,ctime string ) row format delimited fields terminated by ','; LOAD DATA LOCAL INPATH '/Users/liuwenqiang/workspace/hive/ods_user_rate_log.txt' OVERWRITE INTO TABLE ods_user_rate_log;現(xiàn)在們分析一下,首先我們是單獨(dú)計(jì)每個(gè)商戶(hù)的,所以我們的子窗口定義條件就是用戶(hù)ID,然后我們跟蹤的是費(fèi)率的隨時(shí)間的變化所以我們是要按照時(shí)間排序,最后我們需要的費(fèi)率發(fā)生了變化的數(shù)據(jù),所以我們需要比較一下原始費(fèi)率和下一次費(fèi)率
第一步:獲取當(dāng)前費(fèi)率和下一次的費(fèi)率
select userid,ctime,rate, lead(rate,1) over (partition by userid order by ctime) new_rate from ods_user_rate_log;第二步:找出費(fèi)率不同的記錄然后返回,需要注意的是,我們還需要需要獲取到發(fā)生變化的時(shí)間
select * from(select userid,ctime,lead(ctime,1) over (partition by userid order by ctime) new_date,rate, lead(rate, 1) over (partition by userid order by ctime) new_rate from ods_user_rate_log) tmp where rate!=new_rate;總結(jié)
lag和lead 主要用來(lái)計(jì)算當(dāng)前行的前后N 行的這種場(chǎng)景,一般情況下我們會(huì)對(duì)數(shù)據(jù)進(jìn)行排序,因?yàn)橹挥性谟行虻那闆r下,前面多少行和后面多少行才有意義
lag和lead 可以用在某些場(chǎng)景下代替自關(guān)聯(lián)的寫(xiě)法
總結(jié)
以上是生活随笔為你收集整理的窗口函数_LAG()与LEAD() 详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 常用找图网
- 下一篇: 《边缘云计算技术及标准化白皮书》