Hive的窗口函数

发布于:2025-07-28 ⋅ 阅读:(19) ⋅ 点赞:(0)

Hive的窗口函数(Window Functions)是其SQL功能的核心亮点之一,用于在分组数据上执行计算,同时保留原始表的行数(不压缩分组)。窗口函数特别适用于排名分析趋势计算移动统计等复杂场景,是处理时间序列数据和多维分析的利器。

一、窗口函数的核心概念

窗口函数的语法结构:

function_name(arg1, arg2...) OVER (
  [PARTITION BY col1, col2...]  -- 分组(类似GROUP BY,但不压缩行数)
  [ORDER BY col3, col4...]      -- 排序(决定窗口内数据的处理顺序)
  [ROWS/RANGE BETWEEN ... AND ...]  -- 窗口范围(可选,定义当前行的关联行)
)
关键组件解析:
  1. 函数部分

    • 聚合函数:如sum()avg()count(),在窗口内执行聚合。
    • 排序函数:如row_number()rank(),生成排名。
    • 分析函数:如lead()lag(),获取前后行数据。
  2. OVER子句

    • PARTITION BY:将数据按指定列分组,窗口函数在每个分组内独立计算。
    • ORDER BY:定义窗口内数据的排序规则,影响函数计算顺序(如累加方向)。
    • 窗口框架(Window Frame)
      • ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW:从分组首行到当前行。
      • ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING:当前行的前1行到后1行(共3行)。

二、窗口函数的分类与应用场景

1. 排名函数

row_number()

  • 功能:为窗口内的每行分配唯一序号(1,2,3…),无重复值。
  • 应用:分组内TopN筛选(如每个用户的最近订单)。
    示例
SELECT 
  user_id, 
  order_time, 
  row_number() OVER (
    PARTITION BY user_id 
    ORDER BY order_time DESC  -- 按订单时间倒序
  ) AS rn
FROM orders;
-- rn=1即为每个用户的最近订单

rank() vs dense_rank()

  • 区别
    • rank():相同值排名相同,后续排名跳号(如1,1,3)。
    • dense_rank():相同值排名相同,后续排名不跳号(如1,1,2)。
      示例
SELECT 
  score,
  rank() OVER (ORDER BY score DESC) AS rk,
  dense_rank() OVER (ORDER BY score DESC) AS drk
FROM students;
-- 若score有两个85分,rk为1,1,3;drk为1,1,2
2. 聚合函数(窗口聚合)

sum() / avg() / count()

  • 功能:在窗口内执行累加、平均、计数,不压缩行数。
  • 应用:计算累计销售额、移动平均。
    示例
SELECT 
  order_date, 
  amount,
  sum(amount) OVER (
    ORDER BY order_date 
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW  -- 累计到当前行
  ) AS cumulative_sum,
  avg(amount) OVER (
    ORDER BY order_date 
    ROWS BETWEEN 2 PRECEDING AND CURRENT ROW  -- 近3天移动平均
  ) AS moving_avg
FROM daily_orders;
3. 分析函数

lead(col, n) / lag(col, n)

  • 功能:获取当前行的前n行(lag)或后n行(lead)数据。
  • 应用:计算环比(如次日与当日的差值)。
    示例
SELECT 
  date, 
  sales,
  lag(sales, 1) OVER (ORDER BY date) AS prev_day_sales,  -- 前一天销量
  lead(sales, 1) OVER (ORDER BY date) AS next_day_sales   -- 后一天销量
FROM sales_data;

first_value(col) / last_value(col)

  • 功能:获取窗口内的第一个值或最后一个值。
  • 注意last_value默认在当前行截止,需显式指定窗口范围到分组末尾。
    示例
SELECT 
  user_id, 
  order_time, 
  amount,
  first_value(amount) OVER (
    PARTITION BY user_id 
    ORDER BY order_time 
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  ) AS first_order_amount,
  last_value(amount) OVER (
    PARTITION BY user_id 
    ORDER BY order_time 
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  ) AS last_order_amount
FROM orders;

三、窗口框架(Window Frame)详解

窗口框架定义了当前行的关联行范围,决定了函数的计算边界。语法:

ROWS/RANGE BETWEEN start AND end
两种框架模式:
  1. ROWS模式:按物理行号定位(不受值影响)。

    • 示例:ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING(当前行的前2行到后1行)。
  2. RANGE模式:按值的范围定位(适用于数值或时间类型)。

    • 示例:RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND CURRENT ROW(当前日期的前7天内)。
常用范围值:
  • UNBOUNDED PRECEDING:分组的第一行。
  • UNBOUNDED FOLLOWING:分组的最后一行。
  • CURRENT ROW:当前行。

四、窗口函数的典型应用场景

场景1:TopN筛选(每个分组的前N条)

需求:找出每个部门薪资最高的前3名员工。
解法

WITH ranked_employees AS (
  SELECT 
    department, 
    name, 
    salary,
    row_number() OVER (
      PARTITION BY department 
      ORDER BY salary DESC
    ) AS rn
  FROM employees
)
SELECT * FROM ranked_employees WHERE rn <= 3;
场景2:移动统计(如7日滚动平均)

需求:计算每日销售额的7日移动平均值。
解法

SELECT 
  date, 
  sales,
  avg(sales) OVER (
    ORDER BY date 
    ROWS BETWEEN 6 PRECEDING AND CURRENT ROW  -- 包含当前行共7天
  ) AS rolling_avg
FROM daily_sales;
场景3:环比/同比分析

需求:计算每日销售额的环比增长率(较前一日)。
解法

SELECT 
  date, 
  sales,
  prev_day_sales,
  ((sales - prev_day_sales) / prev_day_sales) * 100 AS growth_rate
FROM (
  SELECT 
    date, 
    sales,
    lag(sales, 1) OVER (ORDER BY date) AS prev_day_sales
  FROM daily_sales
) t;
场景4:累计分布(如计算累计占比)

需求:计算每个用户的订单金额占其总金额的累计百分比。
解法

SELECT 
  user_id, 
  order_id, 
  amount,
  sum(amount) OVER (
    PARTITION BY user_id 
    ORDER BY order_time 
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
  ) / sum(amount) OVER (PARTITION BY user_id) AS cumulative_percent
FROM orders;

五、性能优化建议

  1. 合理使用窗口框架

    • 避免使用UNBOUNDED FOLLOWING,可能导致全量数据缓存。
    • 优先使用ROWS模式(比RANGE更高效)。
  2. 分区与排序优化

    • PARTITION BY的列尽量选择数据分布均匀的字段,避免数据倾斜。
    • 窗口函数的计算可能触发多次排序,可结合CTE(公共表表达式)提前排序。
  3. 大表计算避免全量窗口

    • 若数据量极大,可先通过WHERE过滤后再应用窗口函数。

六、与GROUP BY的对比

特性 窗口函数(Window Functions) GROUP BY
输出行数 保留原始行数(不压缩) 压缩为分组后的行数
聚合方式 对每个分组内的每行数据单独计算 对每个分组汇总为单行结果
典型应用 排名、移动统计、累计计算 分组求和、平均值等
能否同时访问原始列 是(可保留未参与分组的列) 否(只能访问分组列或聚合值)

总结

窗口函数是Hive处理复杂分析需求的核心工具,通过PARTITION BYORDER BY和窗口框架的组合,可实现排名分析、趋势计算、时间序列处理等高级功能。实际应用中,需根据业务场景选择合适的窗口函数类型(排名、聚合、分析),并注意窗口框架的边界定义,以确保计算结果符合预期。


网站公告

今日签到

点亮在社区的每一天
去签到