Hive窗口函数/内部、外部表

本文详细介绍了Hive窗口函数的使用,包括窗口函数的实现原理、常用序列函数如NTILE、ROW_NUMBER、RANK、DENSE_RANK、LAG和LEAD,以及FIRST_VALUE和LAST_VALUE。同时,讨论了Hive中的内部表和外部表的区别,强调了在数据管理和删除时的注意事项,提供了数据备份和管理的建议。

Hive窗口函数

在SQL处理中,窗口函数都是最后一步执行,而且仅位于Order by字句之前。

聚合函数+over
查询在2015年4月份购买过的顾客及总人数,使用窗口函数实现

select name,count(*) over ()
from t_window
where substring(orderdate,1,7) = '2015-04'

partition by子句
Over子句之后第一个提到的就是Partition By。Partition By子句也可以称为查询分区子句,非常类似于Group By,都是将数据按照边界值分组,而Over之前的函数在每一个分组之内进行,如果超出了分组,则函数会重新计算。
查看顾客的购买明细及月购买总额,可以执行如下的sql

select name,orderdate,cost,
sum(cost) over(partition by month(orderdate))
from t_window

order by子句
order by子句会让输入的数据强制排序(窗口函数是SQL语句最后执行的函数,因此可以把SQL结果集想象成输入数据)。Order By子句对于诸如Row_Number(),Lead(),LAG()等函数是必须的,因为如果数据无序,这些函数的结果就没有任何意义。因此如果有了Order By子句,则Count(),Min()等计算出来的结果就没有任何意义。

sum() 中加order by 和不加order by的区:加了的话是一个累加的过程,不加是分组汇总,比如有10条记录,算的每个值都一样是(是和),加了order by 就是每一行数据依次累加。一般求sum,不加order by

 select prov_name, city_name, val_cnt, 
 sum(val_cnt) over(partition by prov_name ORDER BY val_cnt) AS rst
 from test_orer_partition_by ;

在这里插入图片描述

 select prov_name, city_name, val_cnt, 
 sum(val_cnt) over(partition by prov_name) AS rst
 from test_orer_partition_by ;

在这里插入图片描述

window子句
已经通过使用partition by子句将数据进行了分组的处理。如果想要更细粒度的划分,需要引入window子句。首先要理解两个概念:

  • 如果只使用partition by子句,未指定order by的话,聚合是分组内的聚合
  • 使用了order by子句,未使用window子句的情况下,默认从起点到当前行

当同一个select查询中存在多个窗口函数时,它们相互之间是没有影响的。每个窗口函数应用自己的规则。window子句:

  • PRECEDING:往前
  • FOLLOWING:往后
  • CURRENT ROW:当前行
  • UNBOUNDED:起点,UNBOUNDED PRECEDING 表示从前面的起点, UNBOUNDED FOLLOWING:表示到后面的终点

按照name进行分区,按照购物时间进行排序,做cost的累加。如下结合使用window子句进行查询

select name,orderdate,cost,
sum(cost) over() as sample1,--所有行相加
sum(cost) over(partition by name) as sample2,--按name分组,组内数据相加
sum(cost) over(partition by name order by orderdate) as sample3,--按name分组,组内数据累加
sum(cost) over(partition by name order by orderdate rows between UNBOUNDED PRECEDING and current row )  as sample4 ,--和sample3一样,由起点到当前行的聚合
sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING   and current row) as sample5, --当前行和前面一行做聚合
sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING   AND 1 FOLLOWING  ) as sample6,--当前行和前边一行及后面一行
sum(cost) over(partition by name order by orderdate rows between current row and UNBOUNDED FOLLOWING ) as sample7 --当前行及后面所有行
from t_window;

窗口函数实现原理

  窗口函数的实现,主要借助一个叫做WindowingTableFunction的Partitioned Table Function (即PTF);PTF的输入可以是:表、子查询或另一个PTF函数输出;PTF输出也是一张表。

select channel, month,
sum(amount),
denserank() over (partition by channel order by sum(amount) desc) as dr,
rank() over(partition by channel order by sum(amount)desc) as r
from sales
group by channel, month;

考虑以上代码,在Hive中具体实现主要有两个阶段:

  • 计算除窗口函数以外所有的其他运算,如:group by,join ,having等。上面的代码的第一阶段即为:
select channel, month, sum(amount) as s from sales group by channel, month;
  • 将上一步的输出作为 WindowingTableFunction 函数的输入,计算对应的窗口函数值。上面代码的第二阶段即为:
select channel, month, s,dr,r from 
WindowingTableFunction(
-- 上一阶段的输出
<select channel, month, sum(amount) as s from sales group by channel, month>,
-- 窗口函数的分区list
partition by channel,
-- 窗口函数的order list
order by s,
-- 窗口函数调用
[r:<rank()>, dr:<denserank()>]
)

窗口函数中的序列函数

序列函数是不支持window子句的。Hive中常用的序列函数有下面几个:

NTILE

  • NTILE(n),用于将分组数据按照顺序切分成n片,返回当前切片值
  • NTILE不支持ROWS BETWEEN, 比如 NTILE(2) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)
  • 如果切片不均匀,默认增加第一个切片的分布

假如想要每位顾客购买金额前1/3的交易记录,便可以使用这个函数.

select name,orderdate,cost,
       ntile(3) over() as sample1 , --全局数据切片
       ntile(3) over(partition by name), -- 按照name进行分组,在分组内将数据切成3ntile(3) over(order by cost),--全局按照cost升序排列,数据切成3ntile(3) over(partition by name order by cost ) --按照name分组,在分组内按照cost升序排列,数据切成3from t_window

row_number/rank/dense_rank
这三个窗口函数的使用场景非常多

  • row_number()从1开始,按照顺序,生成分组内记录的序列,row_number()的值不会存在重复,当排序的值相同时,按照表中记录的顺序进行排列
  • RANK() 生成数据项在分组中的排名,排名相等会在名次中留下空位
  • DENSE_RANK() 生成数据项在分组中的排名,排名相等会在名次中不会留下空位

注意: rank和dense_rank的区别在于排名相等时会不会留下空位

SELECT 
cookieid,
createtime,
pv,
RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn1,
DENSE_RANK() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rn2,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY pv DESC) AS rn3 
FROM lxw1234 
WHERE cookieid = 'cookie1';

LAG和LEAD函数
为常用的窗口函数,可以返回上下数据行的数据。以订单表为例,假如想要查看顾客上次的购买时间可以这样去查询

select name,orderdate,cost,
lag(orderdate,1,'1900-01-01') over(partition by name order by orderdate ) as time1,
lag(orderdate,2) over (partition by name order by orderdate) as time2
from t_window;

time1取的为按照name进行分组,分组内升序排列,取上一行数据的值。time2取的为按照name进行分组,分组内升序排列,取上面2行的数据的值,注意当lag函数未设置行数值时,默认为1行。设定取不到时的默认值时,取null值。lead函数与lag函数方向相反,取向下的数据.

first_value和last_value
first_value取分组内排序后,截止到当前行,第一个值
last_value取分组内排序后,截止到当前行,最后一个值

select name,orderdate,cost,
first_value(orderdate) over(partition by name order by orderdate) as time1,
last_value(orderdate) over(partition by name order by orderdate) as time2
from t_window

Hive内部表和外部表

外部表(external table)
有external修饰,表数据保存在HDFS上,该位置由用户指定。删除表时,只会删除表的元数据,所以外部表不是有Hive完全管理的

内部表(internal table/managed table)
没有external修饰,表数据保存在Hive默认的路径下,数据完全由Hive管理,删除表时元数据和表数据都会一起删除。

区别

  • 内部表数据由Hive自身管理,外部表数据由HDFS管理
  • 外部表的表数据存储位置由用户指定,而内部表的数据默认存储位置为/user/hive/warehouse/数据库名.db/数据文件名
  • 删除外部表时,只会删除表的元数据,表数据仍然存储在HDFS中,删除内部表时,元数据和表数据都会删除
  • 对内部表修改时会同步到元数据,而对外部表结构和分区修改时,需要进行修复MSCK REPAIR TABLE table_name

使用场合

  • 希望做数据备份并且不经常改变的数据,存放在外部表可以减少失误操作
  • 数据清洗转换后的中间结果,可以存放在内部表,因为Hive对内部表支持的功能比较全面,方便管理
  • 处理完成的数据由于需要共享,可以存储在外部表,这样能够防止失误操作,增加数据的安全性

外部表数据变动怎么和外部表建立联系?
三种方式:直接load、msck repair、上传数据之后添加分区

<think> 首先,用户的问题是:“我想查询在Hive的HAVING子句中能否使用窗口函数 请问Hive的HAVING子句中能否使用窗口函数”。这是一个直接的查询,我需要基于Hive SQL的知识来回答。 回顾之前的交互:用户提供了引用内容,其中引用[1]讨论了HiveSQL中窗口函数的应用,特别是条件判断语句嵌套窗口函数。引用[2]解释了窗口函数的定义和基本用法。用户的上一个问题或上下文似乎是关于在HAVING子句中使用窗口函数。 从系统级指令: - 所有行内数学表达式必须使用$...$格式,但这里的问题是关于SQL的,所以可能不需要数学表达式。 - 独立公式使用$$...$$格式,但同样,SQL相关。 - 使用中文回答。 - 生成相关问题。 - 在引用段落末尾添加引用标识,如[^1]。 - 回答结构清晰,逐步解决问题。 - 参考站内引用:用户提供了引用[1]和[^2],我应该引用这些内容。 用户的引用: - 引用[1]:讨论了窗口函数与条件判断语句的执行顺序,指出在CASE WHEN或IF语句中嵌套窗口函数时,条件判断在窗口函数之后执行。它还提到partition by子句中可以嵌套条件判断语句。 - 引用[^2]:解释了窗口函数的概念,并给出了一个例子,显示窗口函数聚合后可以访问各个行。 用户的问题是:在Hive的HAVING子句中能否使用窗口函数? 关键点: - Hive的HAVING子句用于过滤分组后的结果,通常与GROUP BY一起使用。 - 窗口函数用于对一组行进行操作,返回聚合值,但不会减少行数;它们通过OVER()子句定义。 在标准SQL中,HAVING子句用于过滤GROUP BY聚合后的结果。窗口函数通常在SELECT子句中使用,因为它们操作的是行级数据。 在Hive中,HAVING子句不能直接包含窗口函数窗口函数是在SELECT或ORDER BY子句中定义的,HAVING是针对GROUP BY后的聚合结果进行过滤。 确认Hive文档或常见用法: - 窗口函数Hive中定义在SELECT列表或ORDER BY部分。 - HAVING子句只能引用GROUP BY中定义的列或聚合函数。 - 窗口函数的结果是在SELECT之后计算的,所以不能在HAVING中使用,因为HAVING在WHERE之后、SELECT之前执行?不,SQL执行顺序是:FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY。 标准SQL执行顺序: 1. FROM 2. WHERE 3. GROUP BY 4. HAVING 5. SELECT 6. ORDER BY 窗口函数通常在SELECT阶段或ORDER BY阶段计算。这意味着在HAVING子句中,还没有计算窗口函数的结果,因为你还没有到SELECT阶段。 因此,在HAVING子句中,不能直接使用窗口函数。 例如: - 如果你想基于窗口函数的计算结果过滤,你应该在子查询或CTE中使用窗口函数,然后在外部查询中使用WHERE或HAVING。 在引用中: - 引用[1]提到:“窗口函数partition by子句中是可以嵌套条件判断语句的”,但这指的是在OVER()子句中使用条件,不是在HAVING中。 - 没有直接提到HAVING。 所以,答案应该是:在Hive中,HAVING子句中不能直接使用窗口函数窗口函数只能在SELECT或ORDER BY子句中使用。 回答结构: 1. 明确回答:是的或否。 2. 解释原因:基于SQL执行顺序。 3. 提供替代方案:例如,使用子查询。 4. 引用提供的参考文献。 5. 生成相关问题。 使用中文回答。 添加引用:在回答中引用参考文献时,在末尾添加[^1]或[^2]。 生成相关问题:基于主题。 最终回答: - 首先,直接回答问题。 - 然后,详细解释。 - 最后,生成相关问题。 </think> ### Hive中HAVING子句能否使用窗口函数? 在Hive SQL中,**HAVING子句中不能直接使用窗口函数**。这是因为SQL查询的执行顺序限制了HAVING子句只能引用GROUP BY分组后的聚合结果或分组列,而窗口函数的计算发生在SELECT阶段之后。让我逐步解释原因、替代方案和实际示例。 #### 1. **原因:SQL执行顺序的限制** - SQL查询的标准执行顺序为: 1. FROM 2. WHERE 3. GROUP BY 4. HAVING 5. SELECT 6. ORDER BY - 窗口函数(如`SUM() OVER()`或`ROW_NUMBER()`)通过`OVER()`子句定义,其计算发生在SELECT阶段(第5步)。这意味着在HAVING阶段(第4步),窗口函数的结果尚未计算,因此无法被引用。 - 相反,HAVING子句只能使用GROUP BY生成的聚合值(如`SUM()`、`COUNT()`)或分组列。窗口函数虽然类似聚合函数,但保留了原始行,因此不适用于HAVING的过滤逻辑[^1][^2]。 #### 2. **替代方案:如何在Hive中实现类似功能** 虽然HAVING子句不能直接使用窗口函数,但可以通过以下方法间接实现: - **使用子查询(Subquery)**:在子查询中先计算窗口函数,然后在外部查询的HAVING或WHERE中过滤。 - **使用通用表表达式(CTE)**:将窗口函数计算放入CTE,再在外部过滤。 - **在SELECT中使用条件判断**:结合`CASE WHEN`或`IF`语句(在SELECT中嵌入窗口函数),然后过滤结果。 **示例场景**:假设有一个员工表`employee`,需要按部门分组后过滤出部门工资总和超过50000的部门,但额外要求基于窗口函数(如部门内工资排名)。 ```sql -- 错误示例:HAVING中直接使用窗口函数(无效) SELECT dept, SUM(salary) AS total_salary FROM employee GROUP BY dept HAVING ROW_NUMBER() OVER(PARTITION BY dept ORDER BY salary DESC) = 1; -- 报错:窗口函数不能在HAVING中使用 -- 正确替代方案:使用子查询 WITH dept_summary AS ( SELECT dept, SUM(salary) OVER(PARTITION BY dept) AS total_window, -- 窗口函数计算 ROW_NUMBER() OVER(PARTITION BY dept ORDER BY salary DESC) AS rnk FROM employee ) SELECT dept, total_window FROM dept_summary WHERE rnk = 1 -- 在WHERE中过滤,而不是HAVING HAVING total_window > 50000; -- 在外部使用HAVING过滤聚合值 ``` 在这个例子中: - 窗口函数在CTE的SELECT子句中计算。 - HAVING子句过滤的是CTE中的聚合值`total_window`,而不是直接操作窗口函数。 #### 3. **注意事项** - **窗口函数的执行顺序**:如引用[1]所述,在`CASE WHEN`或`IF`语句中嵌套窗口函数时,条件判断在窗口函数之后执行。但这不是针对HAVING子句的,而是SELECT内部的逻辑[^1]。 - **partition by与group by的区别**:窗口函数的`PARTITION BY`类似于`GROUP BY`,但不会减少行数(保留原始行),而`GROUP BY`会折叠行。因此,HAVING仅适用于`GROUP BY`的聚合结果[^2]。 - **Hive版本兼容性**:Hive 2.0+支持窗口函数,但语法限制与标准SQL一致。如果尝试在HAVING中使用窗口函数Hive会抛出解析错误(如`SemanticException`)。 #### 4. **引用内容参考** - 引用[1]强调了窗口函数与条件判断的执行顺序,但未直接讨论HAVING子句;它指出在`PARTITION BY`中可嵌套条件判断,这适用于OVER子句,而非HAVING[^1]。 - 引用[2]解释了窗口函数的本质(OLAP函数),并对比了其与普通聚合函数的区别,强调窗口函数需要OVER子句并在SELECT阶段生效[^2]。 总之,在Hive中避免直接在HAVING子句中使用窗口函数。优先使用子查询或CTE来实现过滤逻辑。如果您有具体查询需求,我可以帮助优化SQL代码。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值