☰
Hive 函数分类详解:高频用法、常见坑点与实战技巧
2026/10/21 7:12:36 网站建设 项目流程

做数据开发这些年,Hive 基本是离线数仓绕不开的一环,不管是日常取数、ETL 清洗还是指标加工,天天都在跟 Hive SQL 打交道。很多初学者刚接触时容易被大量函数劝退,觉得又多又杂,记不住也不知道该用哪个。其实 Hive 函数的核心就那么几大类,把最常用的几十个吃透,日常 90% 以上的需求基本都能覆盖。我这篇就把工作里真正高频的 Hive 函数按类目整理一遍,结合实际场景讲清楚它们的用法、坑点和选择逻辑,希望能帮你建立一张属于自己的“函数地图”。

1. 先给 Hive 函数分个类

Hive 函数数量庞大,但按用途基本可以分成这几类:数值计算、字符串处理、日期时间、条件判断、聚合统计、窗口分析、行列转换和 JSON 数据处理。每一类对应一类典型的业务问题。

我在实际项目中见过不少人犯同一个毛病——遇到问题就去搜函数,搜到一个能出结果的就直接用,从来不看有没有更合适的替代方案。结果往往是 SQL 写出来能跑,但性能差、逻辑绕,尤其在数据量大或者逻辑复杂的时候,问题会被无限放大。

所以在正式开始之前,我建议你先建立一个“分类思维”:拿到需求先判断它属于哪一类问题,再去找对应的函数族。这样做有两个好处,第一是记忆负担小,因为函数之间往往有相似的使用模式,比如窗口函数是 over 开头的固定结构,日期函数大多围绕格式化与差值计算展开;第二是排查问题快,结果不对时你能快速锁定可疑范围。

下面几节就按这个分类逻辑逐个展开。

2. 数值函数:别小看 round 的精度陷阱

数值计算在 Hive 里看着简单,但一涉及精度,坑立刻就来。

2.1 最常用的几个数值函数

  • round(double a, int n):四舍五入保留 n 位小数。注意 Hive 的 round 是“四舍五入”,不是银行家舍入,这跟很多金融系统的要求不一样。
  • floor(double a):向下取整,取不大于 a 的最大整数。
  • ceil / ceiling(double a):向上取整,取不小于 a 的最小整数。
  • abs(double a):绝对值。
  • rand(int seed):返回 0 到 1 之间的随机数,可以传随机种子。这个函数在做抽样、打散数据分布时非常有用。
  • pow / power,sqrt:幂运算和平方根。
  • greatest / least:返回一组值中的最大值/最小值。

2.2 round 的坑与替代方案

有一次我在处理金额字段时发现,某张订单表的金额汇总后总差几分钱。查了半天发现是 ETL 里用了 round 来做分摊,而部分分摊明细在精度边界上出现了问题。

round 函数在处理像 2.675 这种值时,期望结果是 2.68,实际可能得到 2.67,原因是底层二进制浮点数无法精确表示某些十进制小数。这个坑对金额计算来说是致命的。

在涉及金额的场景,我一律建议:先把数值放大成整数再计算,最后再除回去,或者改用 decimal 类型。Hive 里 decimal 是精确十进制类型,定义方式是decimal(10, 2),能有效规避浮点精度问题。

另外一个实操细节是 round 的第二参数可以省略,省略时表示取整。但在 Hive 的老版本里,round(3.5)返回 4,这个没问题,可是round(2.5)也返回 3,也就是远离零的方向取整,跟很多编程语言不一样,这属于历史行为,建议显式写清楚参数。

2.3 rand 的实际用途

rand() 在取数阶段用处很大。比如要给用户随机分桶做 A/B 测试,可以直接用rand() * N再 floor,把用户均匀分到 N 个桶里;又比如在大表 join 时为了防止数据倾斜,可以对 key 加盐,加盐的随机部分就来自 rand()。

但要注意 rand 每次执行结果都不同,如果需要结果可复现,必须传固定种子,比如rand(2024)。有一些同学跑数发现两次结果不一致,就是忽略了这个点。

3. 字符串函数:正则表达式是绕不过的必修课

字符串处理是 Hive 日常用得最频繁的一类函数,特点是量大但模式固定。

3.1 基础字符串操作

  • length:取长度。
  • concat / concat_ws:拼接字符串。concat 直接拼接多个字段,如果任何一个参数为 null,结果就是 null;concat_ws 可以指定分隔符,并且会跳过 null 值,这个差别很关键。
  • substr / substring:截取子串,Hive 的索引从 1 开始,不是 0。
  • upper / lower:大小写转换。
  • trim / ltrim / rtrim:去除空格,注意 trim 默认只去空格,不去制表符和换行符,需要一起去的话要用正则替换。
  • lpad / rpad:左/右填充到指定长度,处理编号补零时常用。
  • repeat:重复字符串。
  • split:按指定分隔符切分,返回数组。

3.2 正则三件套

  • regexp_replace(string A, string B, string C):把 A 中匹配正则 B 的部分替换为 C。
  • regexp_extract(string A, string B, int idx):从 A 中提取匹配正则 B 的第 idx 个分组。
  • regexp / rlike:判断字符串是否匹配指定正则,返回布尔值。

这一组函数是清洗数据的核心工具。比如从一堆混杂文本里抽手机号、邮箱,或者把日期格式统一,都得靠它们。

一个容易踩的坑是:Hive 走的是 Java 正则语法,所以反斜杠转义要写两个,比如匹配小数点要写\\.,匹配数字要写\\d+。这个跟 Python、SQL 的正则习惯都不一样,新手经常在这里报错或匹配不到。

regexp_extract 的第三个参数很多初学者不理解,它指的是返回第几个括号分组的结果。比如regexp_extract('abc123', '([a-z]+)([0-9]+)', 1)返回 abc,第 2 个参数返回 123。如果索引传 0,返回整个匹配串。

3.3 字符串函数常见坑点

字符串拼接那个 null 陷阱值得再强调一次。concat('a', null)的结果是 null,不是 ‘a’。很多统计结果莫名其妙变空,查到最后都是因为某个字段有空值。解决方案有两个:要么用concat_ws,要么提前用nvl把 null 转成空串。

substr 的索引从 1 开始也经常坑到从别的语言转过来的开发者。substr('abcdef', 2, 3)结果是 ‘bcd’,不是 ‘abc’。如果你拿 Python 的习惯去套,一定出错。

4. 日期时间函数:时区与格式统一是核心痛点

日期函数使用频率极高,因为数仓里几乎每张表都带日期分区,几乎每个需求都涉及时间范围的过滤和统计。

4.1 必会的日期函数

  • from_unixtime(bigint t, string format):将 Unix 时间戳转成指定格式的日期字符串,format 里用 yyyy-MM-dd HH:mm:ss 这种模式。注意是 yyyy 不是 YYYY,大写的 Y 代表“周年份”,跨年时可能得到错误年份。
  • unix_timestamp(string date, string format):反向操作,把日期字符串转成时间戳。
  • current_date / current_timestamp:取当前日期/时间。注意 current_timestamp 带时区。
  • datediff(string enddate, string startdate):返回两个日期相差的天数,结果是 enddate 减 startdate。这个方向经常有人搞反。
  • date_add / date_sub(string date, int n):日期加/减 n 天。也可以直接写date '2024-01-01' + 1这种语法。
  • date_format(string date, string format):格式化日期字符串,功能跟 from_unixtime 类似但入参不同。这个函数底层走的是 Java SimpleDateFormat。
  • year / month / day / hour / minute / second:提取日期时间分量。
  • weekofyear:计算一年中的第几周,但这个函数的周起始日是周一,跟 MySQL 的默认行为不一样。
  • last_day:返回当月最后一天,常用于月维度的自然月划分。
  • trunc(string date, string unit):截断日期到指定精度,trunc 支持 ‘YEAR’、‘MONTH’、‘DAY’ 等,做月末、季末统计很方便。

4.2 时区问题

某次我排查一个数据延迟问题,找到最后发现是系统时间和 Hive 所在集群的时区设置不一致,导致取数窗口差了几个小时。Hive 服务端的时区由hive.local.time.zone等参数控制,默认通常是 UTC,而业务侧往往期望的是本地时区。

处理这类问题有两条路。第一是在会话级别设置时区,执行set time zone=Asia/Shanghai;;第二是在函数层面处理,比如用from_utc_timestamp和to_utc_timestamp做显式转换。我个人更推荐前者,因为全局统一更不容易出错,但要注意这个设置会影响所有日期函数的返回值,改动前要评估影响面。

4.3 日期格式的统一策略

数仓里最痛的其实是日期格式不统一。上游表有 yyyyMMdd 的,有 yyyy-MM-dd HH:mm:ss 的,还有纯时间戳的。我的建议是在 ODS 层就把日期统一成 yyyy-MM-dd 格式,后续所有层都沿用一个标准。这个统一动作价值极高,能省掉后面大量重复的转换逻辑。

一个标准做法:

select from_unixtime(unix_timestamp(dt_str, 'yyyyMMdd'), 'yyyy-MM-dd') as dt_std from source_table;

如果你的源数据里有脏格式,比如混入了 null 或乱字符串,unix_timestamp会返回 null,结果也会被污染。稳妥的做法是先清洗一层,再做转换,不要指望一条 SQL 解决所有脏数据。

5. 条件函数:NULL 处理的哲学问题

条件函数不复杂,但 NULL 的处理方式会直接影响统计口径。

5.1 常用条件函数

  • if(boolean test, T a, T b):最基础的分支判断,满足条件返回 a,否则返回 b。
  • case when ... then ... else ... end:标准的 SQL 分支,可处理多条件。
  • coalesce(T a, T b, ...):返回参数列表中第一个非 NULL 的值。这个函数写 ETL 时太常用了,比如默认值填充、多字段取值优先级。
  • nvl(T a, T b):如果 a 为 NULL 则返回 b,否则返回 a。可以理解为 coalesce 的二元简化版。
  • nullif(T a, T b):如果 a = b 则返回 NULL,否则返回 a。这个函数在做除法防错时非常好用。

5.2 经典场景:防除零

计算转化率、占比这些指标时,最怕的就是分母为 0。直接除会报错或者返回 NULL,用 nullif 可以把 0 转成 NULL,进而用 nvl 兜底:

select pay_cnt, click_cnt, nvl(pay_cnt / nullif(click_cnt, 0), 0) as pay_rate from stats_table;

这个写法简洁而且语义清晰。nullif(click_cnt, 0) 在 click_cnt 为 0 时返回 NULL,除法结果变成 NULL,再被 nvl 替换成 0,整个链路不会报错。

5.3 case when 与 if 的使用边界

case when 可以处理多个条件分支并支持范围判断,if 更适合单个简单条件。有人在只需要一个判断时也写 case when,不是不行,但可读性上不如 if 直接。

另一个更隐蔽的问题是:case when 的各个分支返回类型必须兼容,否则 Hive 会尝试做隐式转换,转换失败直接报错。比如一个分支返回字符串,另一个分支返回数值,Hive 可能会把数值转成字符串,也可能直接抛异常,这跟版本有关。我建议在编写时就保持类型统一,避免踩这个坑。

6. 聚合函数:count(distinct) 是性能杀手

聚合函数是统计分析的基石。Hive 里最常用的是 sum、count、avg、max、min,以及 collect_list、collect_set 这类不太起眼但实际非常有用的函数。

6.1 聚合函数基础

  • sum(column):求和,自动忽略 NULL。
  • count(1) / count(*) / count(column):count(1) 和 count(*) 统计行数,count(column) 只统计该列非 NULL 的个数。这个区别在空值多的字段上结果完全不同。
  • avg(column):平均值,自动忽略 NULL。
  • max / min:最大最小值。
  • collect_list(column):把同一个分组内的值收集成数组,不做去重。
  • collect_set(column):去重后收集成数组。

6.2 count(distinct) 的性能陷阱

count(distinct column) 是其中最危险的写法。这种场景有一个处理口径问题:当业务上需要 distinct 时,如果并发不高还好,一旦数据量上了亿,count(distinct) 经常导致数据倾斜或 OOM。

它的原理是 count(distinct) 在 Hive 中会被转成一个单独的 MR 或 Spark Stage,所有相同 key 的数据必须集中到同一个 reducer,这个 reducer 就成了瓶颈。数据量越大越明显。

实际业务中,我会用两阶段去重来替代,比如先做 group by 去重,再外面包一层 count:

select count(1) from ( select user_id from user_visit_log group by user_id ) t;

这种写法在数据量大时明显比 count(distinct user_id) 要稳定。代价是多一次 shuffle,但换来的是可扩展性。尤其是做用户数、订单数这类大基数去重指标统计时,两阶段方案几乎是必选。

6.3 collect_list 的用途

collect_list 和 collect_set 在做列表聚合时很实用,比如把某个用户的多个标签拼成数组,或者配合 concat_ws 把明细拼成字符串。

一个小坑是 collect_list 不去重且结果顺序不保证,如果要保证顺序,可以在 collect 前显式排序,或者用 sort_array 对结果排序。还有一个更隐蔽的问题:collect_list 过程中如果组内某行为 NULL,这个 NULL 会污染整个集合,导致后续炸开时多出 NULL 行。稳妥做法是提前过滤,例如collect_list(if(col is not null, col, null)),或者用 where 条件过滤掉 NULL 行。

7. 窗口函数:分组取 TopN 的利器

窗口函数是分析函数的核心,也是我认为区分“会写 SQL”和“写得好 SQL”的分水岭。

7.1 常用窗口函数速览

  • row_number() over(...):给每一行一个唯一的递增序号,从 1 开始。
  • rank() over(...):排名,有并列时跳号,比如 1、1、3。
  • dense_rank() over(...):排名,并列不跳号,比如 1、1、2。
  • ntile(n) over(...):把分组内数据分成 n 份,常用于分位数计算。
  • lag(column, n, default) over(...):取分组内当前行往前第 n 行的值。
  • lead(column, n, default) over(...):取分组内当前行往后第 n 行的值。
  • sum() / avg() over(...):累加和、移动平均等,配合 rows between 指定窗口范围。

7.2 row_number 使用场景

row_number 最常见的场景是“每个分类下取前 N 条”。比如统计每个品类销量最高的商品,写法是:

select category_id, product_id, sales_amt, rn from ( select category_id, product_id, sales_amt, row_number() over (partition by category_id order by sales_amt desc) as rn from product_sales_daily ) t where rn <= 10;

注意 row_number 生成的序号从 1 开始,不是从 0 开始。很多同学拿 Python enumerate 的习惯来套,结果位置对不上。

7.3 over 子句的核心语法

over 子句里三部分需要理解清楚:partition by 负责分组,order by 负责组内排序,rows between 负责定义窗口边界。

rows between 的常见写法是:

  • rows between unbounded preceding and current row:从分组第一行到当前行,做累计值。
  • rows between 3 preceding and current row:当前行和前 3 行,共 4 行,做移动平均。
  • rows between current row and unbounded following:从当前行到组内最后一行。

这个窗口边界定义是窗口函数最容易出错的地方。order by 存在时,默认窗口范围是rows between unbounded preceding and current row;没有 order by 时,默认是整个分组。理解这个默认行为能避免很多莫名奇妙的统计结果。

7.4 distribute by + sort by 的配合

窗口函数在 reduce 阶段对数据分布有要求:同一 partition by 分组的行必须进入同一个 reducer。Hive 中可以用 distribute by 显式控制数据分布,再用 sort by 控制 reducer 内部排序。

如果只是在一个 reducer 内做全局排序,可以直接 order by;但如果数据量很大,order by 会启用单 reducer 全局排序,性能压力大。这时可以用 distribute by + sort by 来完成“分组内有序”的效果,同时能并行处理。这个优化在大数据量 TopN 计算中效果非常明显。

8. 行列转换:explode 与 lateral view

行转列和列转行是 ETL 里最考验基本功的操作。如果你在处理嵌套数据结构、标签数据或日志数据,几乎离不开 explode 和 lateral view。

8.1 explode 函数

explode 是 Hive 内置的 UDTF,把数组或者 Map 类型的字段炸开成多行。比如一个用户的标签字段是数组格式,炸开后每个标签单独一行:

select user_id, tag from user_tag_table lateral view explode(tag_list) t as tag;

这个例子里的 lateral view 是必须的,它的作用是为 explode 生成的每一行关联原表行的其余字段。如果只写select explode(tag_list) from ...,你是拿不到同行的 user_id 的。

8.2 posexplode 与处理多个数组

当需要同时展开两个数组,并且保证顺序对应时,可以用 posexplode。它能返回数组元素和对应的位置索引,然后通过索引关联另一个数组:

select user_id, idx, tag from user_tag_table lateral view posexplode(tag_list) t as idx, tag;

更复杂一点的场景是炸开两个独立数组,比如商品的图片和描述一一对应。做法是先炸开第一个数组拿到索引,再用索引去取第二个数组的元素。这种场景用 posexplode 比单纯 explode 高效很多,值得记下来。

8.3 多次 lateral view 的笛卡尔注意点

一个表如果做了两次 lateral view,比如同时炸开数组 A 和数组 B,结果行数是 A 的长度乘 B 的长度,这是笛卡尔积,如果 A、B 都很大,结果集爆炸式增长。有同学在这里踩过大坑,本来是想对 A 和 B 按索引对齐,结果出来一堆重复数据。

遇到这种需求,正确姿势是像上面一样先炸开一个并拿到索引,再用索引取另一个,而不是同时炸两个。或者在炸开时直接通过 where 条件过滤掉不需要的组合,总之要明确控制结果集规模。

8.4 列转行:collect 与 explode 的逆操作

列转行的反向操作是把多行聚合成一行。比如把用户的多条行为记录拼成一个数组,然后再拼接成字符串,这段操作可以用 collect_set 配合 concat_ws 完成:

select user_id, concat_ws(',', collect_set(event_type)) as event_types from user_event_log group by user_id;

这里需要注意 collect_set 的结果是无序的,如果业务上对顺序有要求,得用别的办法。先排序再 collect 在 Hive 中不太好写,一般做法是加一个排序字段,collect 后用 sort_array 排序,或者用 row_number 先编号再 collect 拼接。

9. JSON 数据处理:get_json_object 与 json_tuple

现在很多上游数据是 JSON 格式上报的,日志表、埋点表几乎全是 JSON 嵌套。Hive 处理 JSON 有两个主力函数。

9.1 get_json_object

get_json_object 的用法是get_json_object(json_string, '$.key')。这里的$.key是 JSONPath 表达式,支持点号取嵌套字段,也支持数组下标,比如$.data.list[0].name。

使用时有几个坑值得注意:

  • 如果 JSON 字段不存在,返回 NULL,不会报错。这意味着你写错字段名时,结果只是 NULL,很难排查。
  • JSONPath 对 key 名大小写敏感,必须跟源数据完全一致。
  • 如果 JSON 字符串本身有语法错误,可能返回 NULL 或抛出异常,取决于 Hive 版本。
  • 字符串中的特殊字符,比如点号或中文 key,可能需要转义或者在表达式中用$."key.name"这种写法。不过 Hive 的 get_json_object 对这类情况的兼容性一般,极端情况还有待测。

9.2 json_tuple

json_tuple 可以一次取多个字段,比多次 get_json_object 效率高:

select user_id, t.name, t.age, t.city from raw_log lateral view json_tuple(json_data, 'name', 'age', 'city') t as name, age, city;

json_tuple 配合 lateral view 的使用方式很固定,但它有一个限制:字段个数和别名个数必须一一对应,漏写会报错。

9.3 JSON 解析的进阶思路

如果 JSON 结构复杂、嵌套深,get_json_object 写起来会又长又乱,可维护性很差。我在实际项目里倾向于把 JSON 解析这一步前置到 ODS 层,用一个专门的 UDF 或者直接在上游用 Spark 解析好,输出成扁平化的明细字段。这样后续 DWD、ADS 层写 SQL 时完全不用关心 JSON 解析细节,逻辑清晰且性能更可控。

另外一个技巧是,调试 JSON 解析时,可以先抽一条样例数据做验证。Hive 里可以这样写:

select get_json_object('{"a": {"b": 1}}', '$.a.b');

先确认表达式结果正确,再套到正式 SQL 上,能节省大量返工时间。

10. 函数使用中的常见问题与排查技巧

这一节我整理一下实际开发中最高频的几类问题,很多都是我刚用 Hive 时反复踩过的坑。

10.1 NULL 传播问题

Hive 里几乎所有内置函数遇到 NULL 输入时,输出都是 NULL,除了几个专门处理 NULL 的函数。这意味着你对一个含 NULL 的列做 concat、加减乘除、字符串截取,结果很可能意外变成 NULL。

排查思路很简单:先用select count(*), count(column) from table;对比总行数和非 NULL 行数,看是否有 NULL 数据;或者直接select * from table where column is null limit 10;抽样检查。

10.2 类型转换报错

Hive 类型转换比较宽松,但不代表不会出错。把字符串转数值时,如果字符串不是合法的数字格式,cast 会返回 NULL 而不是报错,这其实是个隐蔽的问题——你以为是转换成功了,实际上整列都是 NULL。我在处理用户输入的上报数据时遇到过这个问题,上游偶尔传了空串或者带单位的文本,cast 之后发现指标全是 NULL。

应对方案:先过滤、后转换,比如where col rlike '^[0-9]+(\\.[0-9]+)?$'确保格式合法后再 cast。或者用 try_cast,Hive 3.0 之后支持,转换失败返回 NULL,配合 nvl 兜底很方便。

10.3 数据倾斜

数据倾斜不是某个函数的问题,而是在使用聚合和 join 时容易出现。表现是任务卡在某个 reducer 上,其余 reducer 都执行完了,进度卡在 99%。

常见的缓解手段包括:

  • 开启 group by 的两阶段聚合,也就是 hive.groupby.skewindata=true,自动做一次局部聚合再整体聚合。
  • 对 join 的 key 加盐,把倾斜的 key 打散。
  • 使用 mapjoin,把小表加载到内存里,避免 shuffle。参数是 hive.auto.convert.join=true 并设置阈值。
  • 单独处理热点 key,比如某个城市的数据量特别大,可以先 filter 出去单独算,再 union 结果。

这个方向要说透需要单独开一篇文章,这里先提个醒:看到 reducers 长时间不结束,第一步就要怀疑数据倾斜,不要盲目加资源。

10.4 小文件过多

写操作产生大量小文件会影响后续读取性能。hive 里经常出现的问题是 insert 语句生成了大量小于 block size 的文件,这时应该考虑开启合并:

set hive.merge.mapfiles=true; set hive.merge.mapredfiles=true; set hive.merge.size.per.task=256000000; set hive.merge.smallfiles.avgsize=16000000;

小文件问题的根源是上游 reducer 数量太多,每个 reducer 都输出一个文件。调整并行度或者用 distribute by 让数据集中,也是治本的方法。

10.5 函数验证的推荐做法

最后分享一个我的实战习惯。遇到不熟悉的函数,不建议直接在正式任务上试,而是先建一个临时表或者用一条简单 SQL 验证行为:

select substr('abcdef', 2, 3), regexp_extract('abc123', '([a-z]+)([0-9]+)', 2), date_format('2024-06-15', 'yyyy/MM/dd'), nvl(null, 'default');

把不确定的函数放到同一句 select 里跑一遍,输出结果一目了然。这个习惯帮我避开了大量文档与实际行为不一致的坑。文档是理想情况,真机跑一遍才是准的。

11. 一个综合案例:从日志明细到用户行为宽表

光列函数不够,我再用一个综合案例收尾,把前面提到的函数串起来。假设我们要从一份埋点日志明细表里,加工出一张用户行为统计宽表,每行是一个用户的汇总指标。

源表结构大致是:

  • user_id:用户 ID
  • event_time:事件时间,字符串
  • page_url:页面 URL
  • action_type:行为类型,比如 view、click、order
  • ext_json:扩展字段,JSON 格式,里面可能包含商品 ID、订单金额

第一步做清洗,统一时间格式并过滤脏数据:

select user_id, from_unixtime(unix_timestamp(event_time, 'yyyy-MM-dd HH:mm:ss'), 'yyyy-MM-dd HH:mm:ss') as event_time_std, page_url, action_type, get_json_object(ext_json, '$.item_id') as item_id, cast(get_json_object(ext_json, '$.amount') as decimal(10, 2)) as amount from ods_event_log where event_time rlike '^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$';

第二步做汇总,按照用户维度聚合行为指标:

select user_id, count(if(action_type = 'view', 1, null)) as view_cnt, count(if(action_type = 'click', 1, null)) as click_cnt, count(if(action_type = 'order', 1, null)) as order_cnt, sum(if(action_type = 'order', amount, 0)) as total_order_amt, max(event_time_std) as last_active_time, concat_ws('|', collect_set(action_type)) as action_type_list from cleaned_log group by user_id;

这里有个小技巧:count(if(...)) 利用的是 count 忽略 NULL 的特性,如果 if 条件不满足返回 NULL,则该行被 count 忽略,效果等于条件计数。这个写法比 sum(case when ... then 1 else 0 end) 更简洁,我日常用得很多。

如果还需要计算用户最近一次下单距离统计日的天数,可以继续用 datediff 处理;如果想取每个用户最近一次浏览的具体页面,可以用窗口函数加 row_number。

这个案例里用到的函数,几乎全部在前面的分类里出现过。你会发现,Hive 函数虽然多,但真正高频的组合套路其实非常固定。把上面这些吃透,再遇到新的需求,大概率不需要临时查文档也能直接写出来。

我个人在实际操作中的体会是,函数熟练度的提升不靠背,靠的是在真实数据上反复试。每踩一个坑,对这个函数的理解就深一层。建议你把自己用错的函数、踩过的坑都记下来,形成自己的速查笔记,比任何文档都管用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询