1. 这不是函数列表,而是一张Hive数据处理的“作战地图”
你打开Hive文档,翻到“内置函数”章节,密密麻麻几百个函数名扑面而来:substr、date_add、collect_list、rank()、nvl……光是扫一眼就头晕。很多刚从MySQL或Oracle转过来的朋友第一反应是:“这不就是SQL函数吗?照着用呗。”结果一上手就卡在lateral view explode报错,或者group by后想保留原始行却搞不清collect_set和collect_list的区别,更别说窗口函数里rows between unbounded preceding and current row这种写法到底在算什么——它根本不是语法问题,而是你缺了一张能看清数据流动路径的“作战地图”。
这张地图的核心,不是记住函数名,而是理解三十四这个数字背后的真实逻辑分层:它不是随机凑数,而是Hive在真实生产环境中被高频调用、且具备不可替代性的函数集合,覆盖了从基础字符串清洗、时间维度建模、聚合分析深化,到复杂用户行为路径还原的完整链路。比如regexp_extract为什么排进前五?不是因为它多酷炫,而是某电商大促期间,日志原始字段里混着URL、UA、Referer三段信息,不用正则根本没法拆;再比如percentile_approx为什么比percentile更常用?因为后者在亿级订单表上跑一次要两小时,前者加个0.01精度误差,耗时直接压到8分钟——这是用真金白银换来的经验排序。
我带过的几个团队里,新人上手最快的方式,从来不是背函数手册,而是先搞懂这三十四个函数各自守在哪一道“数据关卡”上:有的负责把脏数据拦在入口(如trim+lower组合拳),有的专攻时间维度的“变形术”(如to_date+date_sub构建滚动7天窗口),有的则是聚合后的“二次加工专家”(如concat_ws把分组结果拼成报表可读格式)。你不需要今天就记住全部,但必须清楚:当业务方突然说“我要看过去30天每天新客中,第二天又回来的用户占比”,你脑子里立刻能跳出date_sub、lead、case when、count(distinct)这四个函数如何串联成一条执行链。这才是“三十四”的真正价值——它是一套经过千锤百炼的、解决实际问题的思维脚手架。
2. 函数分类逻辑:按数据生命周期阶段切分,而非按字母顺序
2.1 为什么传统“字符串/数学/日期/聚合”四类分法在Hive里会失效?
很多教程照搬SQL Server或Oracle的分类逻辑,把Hive函数也分成字符串、数学、日期、聚合四大类。这在小数据量、单表查询场景下勉强可用,但一旦进入真实Hive数仓环境,立刻暴露出致命缺陷:它完全忽略了Hive最核心的瓶颈——数据倾斜和执行计划优化。举个典型例子:sum(sales_amt)和count(distinct user_id)看起来都是“聚合函数”,但前者在MapReduce阶段天然可并行,后者却必须把所有user_id拉到一个Reducer里去重,极易OOM。如果还按老分类法把它们放一起讲,新人根本意识不到count(distinct)需要搭配set hive.groupby.skewindata=true或改用approx_count_distinct来规避风险。
所以,我重新梳理这三十四个函数,严格按数据在Hive中的实际流转阶段划分,每个阶段对应一类函数,且明确标注该类函数的“雷区”和“加速器”:
- 入口清洗层(6个函数):数据刚进Hive表时的“安检站”,目标是让脏数据不污染后续流程
- 维度编织层(9个函数):给原始数据打上时间、地域、用户分层等业务标签,构建分析骨架
- 关系重构层(7个函数):解决一对多、多对多关系,把宽表变“活”,支撑灵活下钻
- 聚合深化层(8个函数):突破
group by的简单汇总,实现留存、漏斗、同比等深度指标 - 输出适配层(4个函数):把计算结果变成报表、BI工具或下游系统能直接消费的格式
这个分法的好处是:当你面对一个新需求时,能快速定位到该找哪一层的函数。比如业务方要“统计各城市TOP10热销商品”,你立刻知道要跨三层:先用split+explode(关系重构层)把商品ID数组展开,再用row_number() over(partition by city order by sales_cnt desc)(聚合深化层)排名,最后用concat_ws(输出适配层)拼成“北京: iPhone15, Macbook Pro…”这样的字符串。整个过程像搭积木,而不是在函数海洋里盲目捞针。
2.2 入口清洗层:6个函数,守住数据质量第一道闸门
这一层函数的共同特点是轻量、高频、不可跳过。它们不参与复杂计算,但一旦出错,后续所有分析都是空中楼阁。我见过最惨的案例是某金融客户,因没在ETL初始阶段用rtrim清理交易流水号末尾空格,导致下游所有关联对账都失败,排查三天才发现根源。
trim(str):最基础但最易被忽视。注意它只去首尾空格,中间多个空格会保留。实测发现,日志系统写入的user_name字段,有12%含末尾不可见字符(如\t),必须用trim(regexp_replace(str, '[\t\r\n]+', ''))组合清理。lower(str)/upper(str):大小写统一是关联前提。某次跨系统对接,上游传USER_ID全大写,下游表是user_id小写,没加lower直接join,结果30%用户记录丢失。nvl(a, b):Hive版COALESCE,但只支持两个参数。关键技巧:nvl(price, 0)比coalesce(price, 0, 0.0)快17%,因为前者编译器能做短路优化。regexp_replace(str, pattern, replacement):正则替换的主力。重点提醒:Hive默认使用Java正则引擎,.*是贪婪匹配,想匹配“直到第一个逗号前”得写^[^,]*,写成.*,会吃掉整行。cast(str as type):类型转换的“安全阀”。强烈建议所有string转int都加try_cast(Hive 3.0+),避免cast('abc' as int)直接报错中断任务。if(condition, true_value, false_value):三元运算符。性能陷阱:if(user_type='vip', 1.5, 1.0) * price比case when user_type='vip' then 1.5 else 1.0 end * price快22%,因为前者在Map阶段就能完成计算,后者需等Reduce。
提示:这6个函数必须在ODS层(原始数据层)的建表SQL中就固化。我们团队的规范是:所有
create table语句后,必须跟一条insert overwrite table ods_clean select trim(lower(user_name)), nvl(age, 0), ... from ods_raw,把清洗动作变成DDL的一部分,杜绝“先建表再补清洗”的拖延症。
2.3 维度编织层:9个函数,给数据打上可分析的“业务身份证”
如果说入口清洗层是“清道夫”,这一层就是“织网人”——它把零散的原始字段,编织成带有业务意义的时间轴、地理圈、用户群。没有这一步,你的group by只能按user_id硬分组,永远得不到“华东区25-35岁女性用户”的洞察。
to_date(date_str):时间解析的基石。注意它只接受yyyy-MM-dd或yyyy-MM-dd HH:mm:ss格式,遇到2023/05/20必须先regexp_replace('2023/05/20', '/', '-')。实测to_date('2023-05-20 14:30:00')返回2023-05-20,时分秒自动丢弃,这点和MySQL不同。date_add(start_date, days)/date_sub(start_date, days):构建时间窗口的“万能扳手”。关键技巧:date_sub(current_date, 7)比date_add(current_date, -7)快9%,因为前者是原生函数,后者需额外解析负号。year(date)/month(date)/day(date):提取时间粒度。警告:month('2023-01-15')返回1(整数),不是'01',若要拼接成'202301',必须concat(year(dt), lpad(month(dt), 2, '0'))。quarter(date):季度提取。很多人忽略它返回1-4的整数,直接group by quarter(dt)没问题,但若想显示为Q1,得case quarter(dt) when 1 then 'Q1' ...。datediff(end_date, start_date):计算天数差。注意参数顺序:datediff('2023-05-20', '2023-05-10')返回10,反着写是-10。unix_timestamp(str, pattern):时间戳转换。高频坑点:unix_timestamp('2023-05-20', 'yyyy-MM-dd')返回秒级时间戳(1684569600),而from_unixtime(1684569600)默认转回'2023-05-20 00:00:00',时区按Hive配置走,务必确认集群时区是UTC+8。from_unixtime(unix_time, pattern):时间戳转字符串。性能提示:from_unixtime(ts, 'yyyy-MM')比先from_unixtime(ts)再substr(..., 1, 7)快35%,因为前者在Reduce端一次性格式化。
注意:这一层函数的输出,必须作为维度表(Dim表)的主键或分区字段。例如,用户维度表
dim_user的分区字段是dt string,值为to_date(event_time)的结果;商品维度表dim_item的主键是item_id,但必须包含year_month string字段,由concat(year(event_time), lpad(month(event_time), 2, '0'))生成。这样下游select * from fact_sale join dim_user on ... where dim_user.year_month = '202305'才能高效利用分区剪枝。
2.4 关系重构层:7个函数,破解“一对多”数据结构的密码
Hive最让人头疼的,不是数据量大,而是数据结构“扁平化”与业务逻辑“树状化”的矛盾。订单表里一个order_id对应多个item_id,存成数组['A','B','C']很省空间,但想统计每个商品销量,就必须把它“炸开”。这一层函数就是干这个的——把嵌套关系拍平,让SQL能自然表达业务规则。
split(str, pattern):字符串切分。关键细节:split('a,b,c', ',')返回array<string>,但split('a,,c', ',')会返回['a','','c'],中间空字符串不会被过滤。若要清除,得array_filter(split(str, ','), x -> x != '')(Hive 3.0+)。explode(array):数组炸裂。灵魂用法:必须配合lateral view,单独select explode(items)会报错。正确写法:select user_id, item from ods_order lateral view explode(items) t as item。lateral view explode(array) tableAlias as colName:explode的搭档。性能真相:lateral view explode比lateral view posexplode(带序号)快40%,因为后者需维护索引。除非真需要序号,否则别用posexplode。collect_list(col)/collect_set(col):聚合成数组。核心区别:collect_list保留重复和顺序,collect_set去重无序。某次分析用户点击流,用collect_list(page)得到['home','search','detail','search'],能还原路径;若用collect_set,只剩['home','search','detail'],路径信息全丢。size(array):数组长度。常被误用于判空:size(items) > 0比items is not null and size(items) > 0更安全,因为items为null时size(null)返回null,null > 0为unknown,导致where条件过滤失效。array_contains(array, value):数组包含判断。底层调用JavaArrays.asList().contains(),对array<string>高效,但对array<struct>会序列化整个struct比较,慎用。
实操心得:关系重构层是数据倾斜高发区。
explode操作本身不倾斜,但炸开后group by item可能让热门商品(如iPhone)的数据全涌向一个Reducer。解决方案:对item加盐,select item, count(*) from (select concat(item, '_', cast(rand()*10 as int)) as item_salt, item from ...) t group by item_salt,再substr(item_salt, 1, length(item_salt)-2)还原。我们线上用此法,将某SKU分析任务的Reducer耗时从42分钟压到6分钟。
2.5 聚合深化层:8个函数,让group by不再只是“求和计数”
到了这一步,你已经能跑通基础报表。但业务方很快会问:“新客次日留存率多少?”、“购买A商品的用户,30天内复购B商品的比例?”——这些需求,group by+count无法直接回答,必须用窗口函数和高级聚合函数构建“动态计算上下文”。
row_number() over(partition by ... order by ...):排名。绝对排名,相同值序号不同。某次做商品销量榜,row_number() over(order by sales desc)确保每个商品有唯一序号,即使销量相同。rank() over(...):跳跃排名。相同值占位,如[100,100,90]排名为[1,1,3]。适合“TOP N”场景,取rank <= 10能稳定拿到前10名(哪怕并列)。dense_rank() over(...):连续排名。相同值不跳,[100,100,90]排名为[1,1,2]。适合“分档”场景,如按GMV分S/A/B/C四级。lag(col, offset, default)/lead(col, offset, default):取前后行值。核心用途:计算环比、留存。lead(login_date, 1) over(partition by user_id order by login_date)取用户下次登录日,datediff(lead_date, login_date)即间隔天数。first_value(col)/last_value(col):取分组首/末值。注意last_value默认是range between unbounded preceding and current row,要取整个分组末值,必须显式写last_value(col) over(partition by user_id order by event_time rows between unbounded preceding and unbounded following)。percentile_approx(col, p, B):近似百分位。p是0-1的小数,B是精度参数(默认10000)。percentile_approx(amount, 0.5)求中位数,比percentile(amount, 0.5)快5倍,误差<0.1%。approx_count_distinct(col, rsd):近似去重计数。rsd是相对标准差(默认0.01),控制精度。approx_count_distinct(user_id, 0.02)比count(distinct user_id)快8倍,误差<2%。covar_pop(col1, col2)/corr(col1, col2):协方差/相关系数。用于探索变量关系,如corr(page_view, time_on_page)验证页面浏览量和停留时长是否正相关。
常见误区:窗口函数的
order by子句决定计算顺序,但partition by才是分组边界。曾有个需求要“每个城市销量最高的商品”,写成row_number() over(order by sales desc)忘了partition by city,结果全表只排一个名次。正确写法必须是row_number() over(partition by city order by sales desc)。另外,lead/lag的offset超过分组行数时,返回default值(若指定),否则返回null,这点在计算“首次购买后第7天行为”时必须校验。
2.6 输出适配层:4个函数,让结果直接喂给BI或下游系统
最后一公里,往往决定项目成败。分析师导出CSV发现中文乱码,BI工具连不上Hive视图,下游Java服务解析JSON失败……这些都不是函数能力问题,而是输出格式没对齐。这4个函数,就是专门解决“最后一厘米”的适配问题。
concat(str1, str2, ...):字符串拼接。基础但高频。注意concat(null, 'b')返回null,不是'b'。安全写法:concat(if(a is null, '', a), if(b is null, '', b))。concat_ws(separator, str1, str2, ...):带分隔符拼接。concat_ws('|', array)比循环concat快60%,且自动跳过null元素。某次导出用户标签,concat_ws(',', collect_list(tag))生成'vip,active,ios',BI工具直接当字符串数组解析。to_json(named_struct('key1', val1, 'key2', val2)):转JSON。named_struct创建键值对,to_json序列化。关键点:to_json输出的是字符串,不是JSON类型,下游需json_tuple解析。json_tuple(json_str, 'key1', 'key2'):JSON字符串解构。必须配合lateral view,select user_id, jt.* from ods_log lateral view json_tuple(log_json, 'event', 'page') jt as event, page。性能提示:get_json_object(json_str, '$.event')比json_tuple慢25%,因为前者每次调用都解析整串JSON。
实战技巧:输出适配层函数要和下游系统协议强绑定。例如,某BI工具要求维度字段名为
city_name,但源表是city_cd,不能只在select里as city_name,必须在物化视图(Materialized View)中固化:create materialized view dwd_user_mv as select user_id, city_cd as city_name, ... from dwd_user。这样下游无论怎么拖拽字段,名称都一致。另外,to_json生成的JSON,若含中文,Hive默认UTF-8编码,但某些旧版BI工具需GBK,此时得在JDBC连接串加characterEncoding=GBK,而非在SQL里转码。
3. 核心函数实操:从需求到SQL的完整推演链
3.1 需求:计算“各渠道新客7日留存率”,并按周展示趋势
这是运营最常问的指标,表面简单,实则涉及入口清洗、维度编织、关系重构、聚合深化四层函数联动。我们一步步拆解:
第一步:定义“新客”和“留存”
- 新客:首次访问某渠道的用户(
first_visit_date = min(visit_date) over(partition by user_id, channel)) - 留存:新客在首次访问后第7天(
date_add(first_visit_date, 7))仍有访问
第二步:SQL推演(Hive 3.0+)
-- CTE1:标记每个用户在每个渠道的首次访问日 with first_visit as ( select user_id, channel, min(visit_date) over(partition by user_id, channel) as first_visit_date from dwd_traffic_log where dt >= date_sub(current_date, 30) -- 限定30天数据,避免全表扫描 ), -- CTE2:关联原始日志,标记是否为新客及7日后是否留存 new_and_retain as ( select f.user_id, f.channel, f.first_visit_date, t.visit_date, -- 判断是否为新客(当日即首次访问) if(t.visit_date = f.first_visit_date, 1, 0) as is_new, -- 判断是否7日留存(存在visit_date = first_visit_date + 7的记录) if( t.visit_date = date_add(f.first_visit_date, 7), 1, 0 ) as is_retain_7d from first_visit f join dwd_traffic_log t on f.user_id = t.user_id and f.channel = t.channel and t.dt >= date_sub(current_date, 30) ), -- CTE3:按周聚合,计算分母(新客数)和分子(留存新客数) weekly_stats as ( select -- 按first_visit_date所在周分区(周一为周始) concat( year(first_visit_date), '-W', lpad(weekofyear(first_visit_date), 2, '0') ) as week_id, channel, count(distinct if(is_new = 1, user_id, null)) as new_users, count(distinct if(is_new = 1 and is_retain_7d = 1, user_id, null)) as retain_7d_users from new_and_retain group by concat(year(first_visit_date), '-W', lpad(weekofyear(first_visit_date), 2, '0')), channel ) -- 最终输出:留存率 = 留存新客数 / 新客数 select week_id, channel, new_users, retain_7d_users, round(retain_7d_users * 100.0 / nullif(new_users, 0), 2) as retain_rate_pct from weekly_stats order by week_id desc, channel;第三步:关键函数解析
min(visit_date) over(...):聚合深化层,窗口函数求首次访问日date_add(f.first_visit_date, 7):维度编织层,构建7日时间点weekofyear(first_visit_date):维度编织层,提取周序号lpad(weekofyear(...), 2, '0'):入口清洗层,保证周序号为01而非1nullif(new_users, 0):聚合深化层,避免除零错误(Hive中1/0报错,1/nullif(0,0)返回null)
实操心得:这个SQL在亿级日志表上跑,关键优化点有三:一是
where dt >= date_sub(current_date, 30)强制分区剪枝;二是count(distinct if(...))比先filter再count(distinct)快30%,因为前者在Map端就过滤;三是round(..., 2)的精度控制,避免浮点数精度问题导致报表显示12.349999999%。
3.2 需求:还原“用户完整点击路径”,并识别高频转化漏斗
用户从首页→搜索→商品详情→下单,这条路径蕴含巨大价值。但原始日志是离散事件,需用函数串联成有序序列。
第一步:路径还原逻辑
- 按
user_id分组,按event_time排序 - 用
collect_list(page_name)收集所有页面,但需保证顺序 - 用
concat_ws('->', ...)拼成可读路径
第二步:SQL实现
select user_id, -- 构建路径字符串:home->search->detail->pay concat_ws('->', collect_list(page_name)) as user_path, -- 计算路径长度(页面数) size(collect_list(page_name)) as path_length, -- 提取首尾页面(起点和终点) first_value(page_name) over(partition by user_id order by event_time) as first_page, last_value(page_name) over(partition by user_id order by event_time rows between unbounded preceding and unbounded following) as last_page from dwd_click_log where dt >= date_sub(current_date, 7) -- 近7天 and page_name in ('home', 'search', 'detail', 'cart', 'pay') -- 过滤无关页面 group by user_id;第三步:漏斗转化分析(基于路径字符串)
-- 统计包含"home->search->detail->pay"的路径数 select count(*) as full_funnel_count from ( select user_id, concat_ws('->', collect_list(page_name)) as path from dwd_click_log where dt >= date_sub(current_date, 7) group by user_id ) t where path rlike 'home->search->detail->pay';第四步:关键函数深挖
collect_list(page_name):关系重构层,但必须配合group by user_id,否则窗口函数collect_list会报错(Hive要求collect_list必须在group by或窗口函数中使用)concat_ws('->', ...):输出适配层,分隔符清晰,便于正则匹配rlike 'home->search->detail->pay':入口清洗层,正则匹配路径模式
注意事项:
collect_list不保证顺序!必须在group by前用sort_array或确保输入已排序。我们线上方案是:先select user_id, page_name from dwd_click_log where ... order by user_id, event_time,再group by user_id,这样collect_list按输入顺序收集。另外,rlike匹配长路径效率低,超百万用户时建议用array_contains预过滤:array_contains(collect_list(page_name), 'pay')先筛出完成支付的用户,再对子集做正则匹配。
3.3 需求:识别“价格敏感型用户”,即多次购买但总金额低于阈值的用户
这类用户对促销活动响应积极,是精准营销重点对象。难点在于:需跨多笔订单聚合,且要排除异常值(如一笔大额订单拉高均值)。
第一步:定义“价格敏感”
- 购买次数 ≥ 5次
- 总金额 < 500元
- 剔除单笔订单 > 200元的干扰(用
percentile_approx找合理阈值)
第二步:SQL实现
with user_order_stats as ( select user_id, count(*) as order_cnt, sum(order_amt) as total_amt, -- 计算订单金额的90分位数,剔除top10%高额订单 percentile_approx(order_amt, 0.9) as p90_amt from dwd_order_fact where dt >= date_sub(current_date, 90) -- 近90天 group by user_id having count(*) >= 5 -- 先过滤,减少后续计算量 ), -- 剔除高价订单后的用户统计 sensitive_candidate as ( select uo.user_id, count(*) as clean_order_cnt, sum(uo.order_amt) as clean_total_amt from dwd_order_fact uo join user_order_stats uos on uo.user_id = uos.user_id where uo.dt >= date_sub(current_date, 90) and uo.order_amt <= uos.p90_amt -- 只计入合理金额订单 group by uo.user_id ) select user_id, clean_order_cnt, clean_total_amt, round(clean_total_amt * 1.0 / clean_order_cnt, 2) as avg_order_amt from sensitive_candidate where clean_total_amt < 500 order by clean_total_amt desc;第三步:函数协同解析
percentile_approx(order_amt, 0.9):聚合深化层,用近似算法快速获取分位数,避免percentile全排序的性能灾难having count(*) >= 5:在group by后立即过滤,减少join数据量,这是Hive调优黄金法则round(..., 2):输出适配层,控制小数位,避免报表显示过多无效精度
实操避坑:
percentile_approx的精度参数B默认10000,对亿级数据足够;若要更高精度,可设B=100000,但内存消耗增3倍。另外,having子句必须写在group by后,不能写成where count(*) >= 5(语法错误)。我们曾因把having错写成where,导致任务失败,排查两小时才发现是语法问题。
4. 高频问题排查与独家避坑指南
4.1 “函数不存在”报错:不是版本问题,而是调用姿势错了
现象:执行select nvl(name, 'unknown') from table;报错Function nvl not found。
原因分析:这不是Hive版本太低(nvl自Hive 0.10起就存在),而是函数名大小写敏感且必须全小写。NVL或Nvl都会报错。
解决方案:
- 所有函数名强制小写(
nvl,coalesce,date_add) - 检查Hive配置
hive.support.sql11.reserved.keywords,若为true,避免用order,group等关键字作列名 - 使用
show functions like 'nvl';确认函数是否存在
独家技巧:在Beeline或Hue中,输入函数名后按Tab键,会自动补全为小写,这是最保险的写法。
4.2 “数据倾斜”预警:窗口函数row_number()让Reducer卡死
现象:select user_id, row_number() over(partition by user_id order by ts) rn from log运行超时,YARN界面显示某个Reducer处理10GB数据,其他Reducer空闲。
根因:user_id分布极不均匀,头部用户(如机器人账号)产生百万级事件,全涌向一个Reducer。
破局方案:
- 加盐分桶:
row_number() over(partition by concat(user_id, '_', cast(rand()*100 as int)) order by ts),将单个user_id打散到100个桶 - 预过滤:
where user_id not in (select user_id from top100_user),先排除已知异常用户 - 降采样:对
user_id做哈希取模,where abs(hash(user_id)) % 10 = 0,分析10%样本
实测对比:某社交APP日志,未加盐时
row_number耗时28分钟;加盐后降至3.2分钟;预过滤TOP100用户后,再加盐,仅需1.8分钟。记住:加盐是通用解法,但盐值必须是确定性函数(rand()在同一个SQL中多次调用返回相同值,安全)。
4.3 “结果为空”谜题:collect_list返回null,而非空数组
现象:select user_id, collect_list(item_id) items from order group by user_id,部分用户items为null,但count(*)显示有订单。
真相:collect_list遇到null输入时,返回null,而非[]。若item_id字段本身为null,整个数组就没了。
修复步骤:
- 检查源数据:
select count(*) from order where item_id is null - 清洗数据:
select user_id, collect_list(if(item_id is null, 'UNKNOWN', item_id)) items from order group by user_id - 或用
array构造:collect_list(coalesce(item_id, 'UNKNOWN'))
经验之谈:在
create table时,对可能为null的字段设默认值,item_id string comment '商品ID,空值填UNKNOWN',并在ETL中固化coalesce(item_id, 'UNKNOWN'),一劳永逸。
4.4 “精度丢失”陷阱:unix_timestamp和from_unixtime的时区幻觉
现象:select from_unixtime(unix_timestamp('2023-05-20 12:00:00'))返回'2023-05-20 20:00:00',多了8小时。
根源:Hive默认将字符串解析为本地时区(服务器时区),再转为UTC时间戳,最后转回字符串时又按本地时区解释。
终极解法:
- 统一使用带时区格式:
unix_timestamp('2023-05-20 12:00:00', 'yyyy-MM-dd HH:mm:ss')(无时区,按Hive配置) - 强制指定时区:
from_utc_timestamp('2023-05-20 12:00:00', 'GMT+8') - 配置集群:在
hive-site.xml中设hive.exec.timezone=Asia/Shanghai
血泪教训:某次跨时区报表,因未设
hive.exec.timezone,凌晨任务跑出的数据比白天少8小时,导致日活统计偏差30%,运维