数仓工具—Hive语法之窗口函数lead和lag(13)

lead 和 lag

前面我们学习的first_value和last_value 取的是排序后的数据截止当前行的第一行数据和最后一行数据

Lag和Lead分析函数可以在一次查询中取出当前行后N行的数据,虽然可以不用排序,但是往往只有在排序的场景下取前面或者后面N 行数据才有意义

这种操作可以代替表的自联接,并且LAG和LEAD有更高的效率。

Lag/Lead(col,n,DEFAULT) 用于统计窗口内当前行往前或者往后第n行值

  • 第一个参数为列名,
  • 第二个参数为往上第n行(可选,默认为1),
  • 第三个参数为默认值(当往上第n行为NULL时候,取默认值,如不指定,则为NULL)

需要注意的是lag 取得是当前行之前的数据,lead 取的实当前行之后的数据

测试数据

我们有这样一个文本user_access_log.txt,里面记录了用户对页面的访问情况id,ctime,url 分别代表了用户ID,访问时间和访问的页面的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

数据说明:Peter 2015-10-12 01:10:00 url1 ,表示Peter在2015-10-12 01:10:00进入了网页url1,即记录的是进入网页的时间。

create table ods_user_log(
    userid string,
    ctime string,
    url string
) row format delimited fields terminated by '\t';
LOAD DATA LOCAL INPATH '/Users/liuwenqiang/workspace/hive/user_access_log.txt' OVERWRITE INTO TABLE ods_user_log;

从例子中学习lead 和 lag

计算当前用户的第一次访问时间以及当前访问时间的上一次访问时间和下一次访问时间

从描述我们知道了分区的条件或者子窗口的定义条件是用户本身,从我们前面的学习中我们知道了第一次访问时间我们可以使用first_value 计算,下面我们看一下这个需求如何实现

select
    userid,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
;

image-20210106120918573

这里我们也对这个结果简单分析一下,对于第一行数据它是没有lag_ctime的,也就是没有上一次访问时间,因为它是第一次访问,对于lead_ctime也就是下一次访问时间2015-11-12 01:15:10

计算用户在每个页面上的停留时长

用户Peter在浏览网页,在某个时刻,Peter点进了某个页面,过一段时间后,Peter又进入了另外一个页面,如此反复,那怎么去统计Peter在某个特定网页的停留时间呢,又或是怎么统计某个网页用户停留的总时间呢?

要计算Peter在页面url1停留的时间,需要用进入页面url2的时间,减去进入url1的时间,即2015-10-12 01:15:10这个时间既是离开页面url1的时间,也是开始进入页面url2的时间。所以我们需要先获取用户在某个页面停留的起始与结束时间

select
    userid,url,ctime as startTime,
  	lead(ctime,1) over(partition by userid order by ctime) as leaveTime
from
    ods_user_log
;

image-20210106131612898

有了进入时间和离开时间我们就很容易计算出用户在特定页面上的停留时间,只需要用离开时间减去进入时间即可

select
    userid,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
;

image-20210106132217687

大家考虑一个问题如果没有lag和lead这样的函数,我们该如何实现这个需求呢,我们可以借助自关联来实现,但是这个自关联就有很多种是想方式了,这里我借助row_number 来实现一种

with a as (
    select
        userid,url,ctime as startTime,
        row_number() over (partition by userid order by ctime ) as rn
    from
        ods_user_log
)
select
    a1.userid userid,a1.startTime startTime,a2.startTime leaveTime,unix_timestamp(a2.startTime )-unix_timestamp(a1.startTime ) as stayTime
from
    a  a1
inner join
    a  a2
on
    a1.userid=a2.userid
    and (a2.rn-a1.rn=1)
;

这里我们主要是使用 (a2.rn-a1.rn=1) 的条件找出了用户进入页面和离开页面的时间

image-20210106133831037

使用场景

lead 和 lag 的使用场景其实是比较多的,尤其我们在计算需要自关联的这种场景下,如果合理使用头是可以很好的提高我们程序的性能和简化我们的写法,下面我们再看一个例子

有个日志表中记录了商户费率变化状态的所有信息用户ID,费率,时间,现在有个需求,要取出按照时间轴顺序,发生了费率变化的数据行,输出数据格式商户ID,当前时间,变化时间,原始费率,变化后的费率

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

现在我们开始建表

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;

现在们分析一下,首先我们是单独计每个商户的,所以我们的子窗口定义条件就是用户ID,然后我们跟踪的是费率的随时间的变化所以我们是要按照时间排序,最后我们需要的费率发生了变化的数据,所以我们需要比较一下原始费率和下一次费率

第一步:获取当前费率和下一次的费率

select
    userid ,ctime,rate , lead(rate,1) over (partition by userid order by ctime)  new_rate
from
     ods_user_rate_log
;

image-20210109101110097

第二步:找出费率不同的记录然后返回,需要注意的是,我们还需要需要获取到发生变化的时间

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
;

image-20210109101548931

总结

  • lag和lead 主要用来计算当前行的前后N 行的这种场景,一般情况下我们会对数据进行排序,因为只有在有序的情况下,前面多少行和后面多少行才有意义
  • lag和lead 可以用在某些场景下代替自关联的写法
<think>首先,用户查询是关于HiveLEADLAG的使用方法功能介绍。用户指定了引用[1][2]的内容,这些是关于这些函的描述。 从引用[1]: - LAG:用于访问前一个行的值。 - 语法LAG(expression [, offset [, default]]) OVER (PARTITION BY ... ORDER BY ...) - 参:expression(列或表达式),offset(默认为1,指定偏移),default(默认为NULL,指定默认值) - PARTITION BY:指定分区列。 - ORDER BY:指定排序列。 - 类似地,LEAD:用于访问后一个行的值。 - 示例SQL:SELECT id, name, score, LAG(score, 1, 0) OVER (PARTITION BY group_name ORDER BY id) AS prev_score, LEAD(score, 1, 0) OVER (PARTITION BY group_name ORDER BY id) AS next_score FROM student_scores; 从引用[2]: - LEAD(col, n, DEFAULT):用于统计窗口内往下第n行值。 - 强调:第一个参是列名,第二个是偏移(默认为1),第三个是默认值(当行为NULL时)。 用户的问题: "我想了解hiveleadlag的相关信息,如使用方法、功能等 请问hiveleadlag的使用方法及功能介绍" 我需要基于系统级指令来构建回答: - **行内学表达式**:使用$...$格式。但在Hive描述中,可能没有直接的学表达式。函本身可能涉及表达式,如偏移量等。如果有变量表示,比如offset,我可以写成$offset$,但这不是学表达式。保持原样。 - **独立公式**:使用$$...$$单独成段。同样,这里可能没有复杂公式。 - **LaTeX语法正确**:确保任何学表达式正确。 - **使用中文回答**:整个回答必须用中文。 - **生成相关问题**:在回答结束后,添加相关问题部分。 - **引用标识**:在引用的段落末尾自然添加引用标识,如[^1]。用户提供了引用[1][2],所以我需要在回答中引用它们。 回答结构: - 我需要清晰、逐步解释LEADLAG。 - 包括方法、功能、语法示例。 - 基于引用内容。 大纲回答: 1. **介绍**:简要解释LEADLAG是什么。 2. **功能**:描述它们的功能。 3. **语法**:详细说明语法,使用引用。 4. **参解释**:解释每个参。 5. **示例**:提供SQL示例。 6. **注意事项**:任何额外提示。 7. **相关问题**:结束时生成相关问题。 确保引用: - 当使用引用内容时,在末尾加[^1]或[^2]。 最终,添加相关问题部分。 相关问题生成: - 基于主题:Hive窗口函数、优化、应用场景等。 - 例如: 1. LEADLAG据分析中的常见用例? 2. 如何优化使用LEADLAG的查询性能? 3. LEADLAG与其他Hive窗口函数的区别? 用中文写。 </think>### HiveLEADLAG的使用方法及功能介绍 在Hive中,LEADLAG窗口函数的一部分,主要用于访问当前行之前(LAG)或之后(LEAD)的行据。这些函据分析中非常有用,例如计算时间序列的滑动窗口值、比较相邻记录等。下面我将逐步解释它们的功能、使用方法、语法示例,确保内容清晰可靠。 #### 功能用途 - **LAG**:用于获取当前行之前的行(即“滞后”行)的值。例如,在时间序列据中,可以用来访问上一时刻的指标值。 - **LEAD**:用于获取当前行之后的行(即“领先”行)的值。例如,可以用于预测下一时刻的据点。 - 这些函通常与OVER子句结合使用,实现基于分区排序的窗口计算。这有助于处理分组据(如按用户分组的时间序列),提高查询效率[^1][^2]。 #### 语法详解 LEADLAG语法类似,都依赖于窗口定义。以下是标准语法格式(基于Hive文档引用内容): 1. **LAG语法**: ```sql LAG(expression [, offset [, default]]) OVER (PARTITION BY partition_columns ORDER BY order_columns) ``` - **expression**:要访问的列或表达式(例如,列名`score`)。 - **offset**(可选):指定要访问的前一个行的偏移量(默认为1)。例如,`offset=1`表示前一行,`offset=2`表示前两行。 - **default**(可选):如果偏移位置超出分区范围(如第一行没有前一行),则返回此默认值(默认为NULL)。例如,`default=0`表示用0填充空值。 - **PARTITION BY**:指定分区列,用于将结果集分组为独立窗口(例如,按用户ID分区)。 - **ORDER BY**:指定排序列,用于确定窗口内的行顺序(例如,按时间戳升序)。 2. **LEAD语法**: ```sql LEAD(expression [, offset [, default]]) OVER (PARTITION BY partition_columns ORDER BY order_columns) ``` - 参LAG类似: - **expression**:要访问的列或表达式。 - **offset**(可选):指定要访问的后一个行的偏移量(默认为1)。例如,`offset=1`表示下一行。 - **default**(可选):当偏移位置超出范围(如最后一行没有下一行)时,返回默认值(默认为NULL)。 - **PARTITION BY****ORDER BY**的功能与LAG相同,确保窗口计算在分组内进行[^1][^2]。 #### 使用方法注意事项 - **基本步骤**: 1. 在SELECT语句中直接调用LAGLEAD。 2. 指定OVER子句定义窗口:PARTITION BY用于分组,ORDER BY用于排序。 3. 可选参offsetdefault可根据需求调整,以避免NULL值边界问题。 - **示例SQL语句**(基于引用[1]的示例): ```sql SELECT id, name, score, LAG(score, 1, 0) OVER (PARTITION BY group_name ORDER BY id) AS prev_score, LEAD(score, 1, 0) OVER (PARTITION BY group_name ORDER BY id) AS next_score FROM student_scores; ``` - **逻辑解析**: - 该查询从`student_scores`表中选取据。 - `LAG(score, 1, 0)`:访问每个分区的当前行之前的行(偏移量1)的`score`值,如果不存在则返回0。 - `LEAD(score, 1, 0)`:访问每个分区的当前行之后的行的`score`值,同样处理默认值。 - 分区按`group_name`分组,排序按`id`顺序。这确保了每个组(如班级)内的据独立处理。 - **输出示例**:假设据为`[(1, 'Alice', 90), (2, 'Bob', 85)]`,则`prev_score`第一行为0(无前一行),`next_score`第一行为85(下一行值)。 - **注意事项**: - 偏移量offset必须是正整,默认为1。如果offset过大(超出分区大小),则返回default值。 - 默认default参是可选的;如果不指定,超出范围时将返回NULL(可能导致计算错误)。建议始终设置default以提高鲁棒性。 - 这些函窗口函数,必须与OVER子句一起使用,否则会报错。 - 在性能上,PARTITION BYORDER BY会影响执行效率;尽量使用索引列优化查询[^1][^2]。 #### 总结 LEADLAGHive中强大的工具,用于访问相邻行据,特别适用于时间序列分析、据比较填充缺失值场景。通过灵活设置偏移默认值,可以处理各种边界条件。如果您有具体据场景,可以提供更多细节以便针对性建议。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不二人生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值