点击上方 "大数据肌肉猿"关注, 星标一起成长

点击下方链接,进入高质量学习交流群

今日更新| 950个转型案例分享-大数据交流群

本文目录:
一、行列转换
二、排名中取他值
三、累计求值
四、窗口大小控制
五、产生连续数值
六、数据扩充与收缩
七、合并与拆分
八、模拟循环操作
九、不使用distinct或group by去重
十、容器--反转内容
十一、多容器--成对提取数据
十二、多容器--转多行
十三、抽象分组--断点排序
十四、业务逻辑的分类与抽象--时效
十五、时间序列--进度及剩余
十六、时间序列--构造日期
十七、时间序列--构造累积日期
十八、时间序列--构造连续日期
十九、时间序列--取多个字段最新的值
二十、时间序列--补全数据
二十一、时间序列--取最新完成状态的前一个状态
二十二、非等值连接--范围匹配
二十三、非等值连接--最近匹配
二十四、N指标--累计去重

一、行列转换

描述:表中记录了各年份各部门的平均绩效考核成绩。
表名:t1
表结构:

a -- 年份
b -- 部门
c -- 绩效得分

表内容

a   b  c
2014  B  9
2015  A  8
2014  A  10
2015  B  7

问题一:多行转多列

问题描述:将上述表内容转为如下输出结果所示:

a  col_A col_B
2014  10   9
2015  8    7

参考答案

select a,max(case when b="A" then c end) col_A,max(case when b="B" then c end) col_B
from t1
group by a;

问题二:如何将结果转成源表?(多列转多行)

问题描述:将问题一的结果转成源表,问题一结果表名为t1_2

参考答案

select a,b,c
from (select a,"A" as b,col_a as c from t1_2 union all select a,"B" as b,col_b as c from t1_2
)tmp;

问题三:同一部门会有多个绩效,求多行转多列结果

问题描述:2014年公司组织架构调整,导致部门出现多个绩效,业务及人员不同,无法合并算绩效,源表内容如下:

2014  B  9
2015  A  8
2014  A  10
2015  B  7
2014  B  6

输出结果如下所示

a    col_A  col_B
2014   10    6,9
2015   8     7

参考答案:

select a,max(case when b="A" then c end) col_A,max(case when b="B" then c end) col_B
from (select a,b,concat_ws(",",collect_set(cast(c as string))) as cfrom t1group by a,b
)tmp
group by a;

二、排名中取他值

表名t2
表字段及内容

a    b   c
2014  A   3
2014  B   1
2014  C   2
2015  A   4
2015  D   3

问题一:按a分组取b字段最小时对应的c字段

输出结果如下所示

a   min_c
2014  3
2015  4

参考答案:

selecta,c as min_c
from
(selecta,b,c,row_number() over(partition by a order by b) as rn from t2
)a
where rn = 1;

问题二:按a分组取b字段排第二时对应的c字段

输出结果如下所示

a  second_c
2014  1
2015  3

参考答案

selecta,c as second_c
from
(selecta,b,c,row_number() over(partition by a order by b) as rn from t2
)a
where rn = 2;

问题三:按a分组取b字段最小和最大时对应的c字段

输出结果如下所示

a    min_c  max_c
2014  3      2
2015  4      3

参考答案:

selecta,min(if(asc_rn = 1, c, null)) as min_c,max(if(desc_rn = 1, c, null)) as max_c
from
(selecta,b,c,row_number() over(partition by a order by b) as asc_rn,row_number() over(partition by a order by b desc) as desc_rn from t2
)a
where asc_rn = 1 or desc_rn = 1
group by a;

问题四:按a分组取b字段第二小和第二大时对应的c字段

输出结果如下所示

a    min_c  max_c
2014  1      1
2015  3      4

参考答案

selectret.a,max(case when ret.rn_min = 2 then ret.c else null end) as min_c,max(case when ret.rn_max = 2 then ret.c else null end) as max_c
from (select*,row_number() over(partition by t2.a order by t2.b) as rn_min,row_number() over(partition by t2.a order by t2.b desc) as rn_maxfrom t2
) as ret
where ret.rn_min = 2
or ret.rn_max = 2
group by ret.a;

问题五:按a分组取b字段前两小和前两大时对应的c字段

注意:需保持b字段最小、最大排首位

输出结果如下所示

a    min_c  max_c
2014  3,1     2,1
2015  4,3     3,4

参考答案

selecttmp1.a as a,min_c,max_c
from
(select a,concat_ws(',', collect_list(c)) as min_cfrom(selecta,b,c,row_number() over(partition by a order by b) as asc_rnfrom t2)awhere asc_rn <= 2 group by a
)tmp1
join
(select a,concat_ws(',', collect_list(c)) as max_cfrom(selecta,b,c,row_number() over(partition by a order by b desc) as desc_rn from t2)awhere desc_rn <= 2group by a
)tmp2
on tmp1.a = tmp2.a;

三、累计求值

表名t3
表字段及内容

a    b   c
2014  A   3
2014  B   1
2014  C   2
2015  A   4
2015  D   3

问题一:按a分组按b字段排序,对c累计求和

输出结果如下所示

a    b   sum_c
2014  A   3
2014  B   4
2014  C   6
2015  A   4
2015  D   7

参考答案

select a, b, c, sum(c) over(partition by a order by b) as sum_c
from t3;

问题二:按a分组按b字段排序,对c取累计平均值

输出结果如下所示

a    b   avg_c
2014  A   3
2014  B   2
2014  C   2
2015  A   4
2015  D   3.5

参考答案

select a, b, c, avg(c) over(partition by a order by b) as avg_c
from t3;

问题三:按a分组按b字段排序,对b取累计排名比例

输出结果如下所示

a    b   ratio_c
2014  A   0.33
2014  B   0.67
2014  C   1.00
2015  A   0.50
2015  D   1.00

参考答案

select a, b, c, round(row_number() over(partition by a order by b) / (count(c) over(partition by a)),2) as ratio_c
from t3
order by a,b;

问题四:按a分组按b字段排序,对b取累计求和比例

输出结果如下所示

a    b   ratio_c
2014  A   0.50
2014  B   0.67
2014  C   1.00
2015  A   0.57
2015  D   1.00

参考答案

select a, b, c, round(sum(c) over(partition by a order by b) / (sum(c) over(partition by a)),2) as ratio_c
from t3
order by a,b;

四、窗口大小控制

表名t4
表字段及内容

a    b   c
2014  A   3
2014  B   1
2014  C   2
2015  A   4
2015  D   3

问题一:按a分组按b字段排序,对c取前后各一行的和

输出结果如下所示

a    b   sum_c
2014  A   1
2014  B   5
2014  C   1
2015  A   3
2015  D   4

参考答案

select a,b,lag(c,1,0) over(partition by a order by b)+lead(c,1,0) over(partition by a order by b) as sum_c
from t4;

问题二:按a分组按b字段排序,对c取平均值

问题描述:前一行与当前行的均值!

输出结果如下所示

a    b   avg_c
2014  A   3
2014  B   2
2014  C   1.5
2015  A   4
2015  D   3.5

参考答案

selecta,b,case when lag_c is null then celse (c+lag_c)/2 end as avg_c
from(selecta,b,c,lag(c,1) over(partition by a order by b) as lag_cfrom t4)temp;

五、产生连续数值

输出结果如下所示

1
2
3
4
5
...
100

参考答案
不借助其他任何外表,实现产生连续数值
此处给出两种解法,其一:

select
id_start+pos as id
from(select1 as id_start,1000000 as id_end
) m  lateral view posexplode(split(space(id_end-id_start), '')) t as pos, val

其二:

selectrow_number() over() as id
from  (select split(space(99), ' ') as x) t
lateral view
explode(x) ex;

那如何产生1至1000000连续数值?

参考答案

selectrow_number() over() as id
from  (select split(space(999999), ' ') as x) t
lateral view
explode(x) ex;

六、数据扩充与收缩

表名t6
表字段及内容

a
3
2
4

问题一:数据扩充

输出结果如下所示

a     b
3   3、2、1
2   2、1
4   4、3、2、1

参考答案

select  t.a,concat_ws('、',collect_set(cast(t.rn as string))) as b
from
(  select  t6.a,b.rnfrom t6left join( selectrow_number() over() as rnfrom  (select split(space(5), ' ') as x) t -- space(5)可根据t6表的最大值灵活调整lateral viewexplode(x) pe) bon 1 = 1where t6.a >= b.rnorder by t6.a, b.rn desc
) t
group by  t.a;

问题二:数据扩充,排除偶数

输出结果如下所示

a     b
3   3、1
2   1
4   3、1

参考答案

select  t.a,concat_ws('、',collect_set(cast(t.rn as string))) as b
from
(  select  t6.a,b.rnfrom t6left join( selectrow_number() over() as rnfrom  (select split(space(5), ' ') as x) tlateral viewexplode(x) pe) bon 1 = 1where t6.a >= b.rn and b.rn % 2 = 1order by t6.a, b.rn desc
) t
group by  t.a;

问题三:如何处理字符串累计拼接

问题描述:将小于等于a字段的值聚合拼接起来

输出结果如下所示

a     b
3     2、3
2     2
4     2、3、4

参考答案

select  t.a,concat_ws('、',collect_set(cast(t.a1 as string))) as b
from
(   select  t6.a,b.a1from t6left join(   select  a as a1 from t6) bon 1 = 1where t6.a >= b.a1order by t6.a, b.a1
) t
group by  t.a;

问题四:如果a字段有重复,如何实现字符串累计拼接

输出结果如下所示

a     b
2     2
3     2、3
3     2、3、3
4     2、3、3、4

参考答案

select a,b
from
(select  t.a,t.rn,concat_ws('、',collect_list(cast(t.a1 as string))) as bfrom(   select  a.a,a.rn,b.a1from(select  a,row_number() over(order by a ) as rn from t6) aleft join(   select  a as a1,row_number() over(order by a ) as rn  from t6) bon 1 = 1where a.a >= b.a1 and a.rn >= b.rn order by a.a, b.a1 ) tgroup by  t.a,t.rnorder by t.a,t.rn
) tt;

问题五:数据展开

问题描述:如何将字符串"1-5,16,11-13,9"扩展成"1,2,3,4,5,16,11,12,13,9"?注意顺序不变。

参考答案

select  concat_ws(',',collect_list(cast(rn as string)))
from
(select  a.rn,b.num,b.posfrom(selectrow_number() over() as rnfrom (select split(space(20), ' ') as x) t -- space(20)可灵活调整lateral viewexplode(x) pe) a lateral view outer posexplode(split('1-5,16,11-13,9', ',')) b as pos, numwhere a.rn between cast(split(num, '-')[0] as int) and cast(split(num, '-')[1] as int) or a.rn = numorder by pos, rn
) t;

七、合并与拆分

表名t7
表字段及内容

a    b
2014  A
2014  B
2015  B
2015  D

问题一:合并

输出结果如下所示

2014  A、B
2015  B、D

参考答案:

selecta,concat_ws('、', collect_set(t.b)) b
from t7
group by a;

问题二:拆分

问题描述:将分组合并的结果拆分出来

参考答案

selectt.a,d
from
(selecta,concat_ws('、', collect_set(t7.b)) bfrom t7group by a
)t
lateral view
explode(split(t.b, '、')) table_tmp as d;

八、模拟循环操作

表名t8
表字段及内容

a
1011
0101
问题一:如何将字符'1'的位置提取出来

输出结果如下所示:

1,3,4
2,4

参考答案

select a,concat_ws(",",collect_list(cast(index as string))) as res
from (select a,index+1 as index,chrfrom (select a,concat_ws(",",substr(a,1,1),substr(a,2,1),substr(a,3,1),substr(a,-1)) strfrom t8) tmp1lateral view posexplode(split(str,",")) t as index,chrwhere chr = "1"
) tmp2
group by a;

九、不使用distinct或group by去重

表名t9
表字段及内容

a     b     c    d
2014  2016  2014   A
2014  2015  2015   B

问题一:不使用distinct或group by去重

输出结果如下所示

2014  A
2016  A
2014  B
2015  B

参考答案

selectt2.year,t2.num
from(select*,row_number() over (partition by t1.year,t1.num) as rank_1from (select a as year,d as numfrom t9union allselect b as year,d as numfrom t9union allselect c as year,d as numfrom t9)t1
)t2
where rank_1=1
order by num;

十、容器--反转内容

表名t10
表字段及内容

a
AB,CA,BAD
BD,EA

问题一:反转逗号分隔的数据:改变顺序,内容不变

输出结果如下所示

BAD,CA,AB
EA,BD

参考答案

select a,concat_ws(",",collect_list(reverse(str)))
from
(select a,strfrom t10lateral view explode(split(reverse(a),",")) t as str
) tmp1
group by a;

问题二:反转逗号分隔的数据:改变内容,顺序不变

输出结果如下所示

BA,AC,DAB
DB,AE

参考答案

select a,concat_ws(",",collect_list(reverse(str)))
from
(select a,strfrom t10lateral view explode(split(a,",")) t as str
) tmp1
group by a;

十一、多容器--成对提取数据

表名t11
表字段及内容

a       b
A/B     1/3
B/C/D   4/5/2

问题一:成对提取数据,字段一一对应

输出结果如下所示

a       b
A       1
B       3
B       4
C       5
D       2

参考答案:

select a_inx,b_inx
from
(select a,b,a_id,a_inx,b_id,b_inxfrom t11lateral view posexplode(split(a,'/')) t as a_id,a_inxlateral view posexplode(split(b,'/')) t as b_id,b_inx
) tmp
where a_id=b_id;

十二、多容器--转多行

表名t12
表字段及内容

a        b      c
001     A/B     1/3/5
002     B/C/D   4/5

问题一:转多行

输出结果如下所示

a        d       e
001     type_b    A
001     type_b    B
001     type_c    1
001     type_c    3
001     type_c    5
002     type_b    B
002     type_b    C
002     type_b    D
002     type_c    4
002     type_c    5

参考答案:

select a,d,e
from
(selecta,"type_b" as d,str as efrom t12lateral view explode(split(b,"/")) t as strunion all selecta,"type_c" as d,str as efrom t12lateral view explode(split(c,"/")) t as str
) tmp
order by a,d;

十三、抽象分组--断点排序

表名t13
表字段及内容

a    b
2014  1
2015  1
2016  1
2017  0
2018  0
2019  -1
2020  -1
2021  -1
2022  1
2023  1

问题一:断点排序

输出结果如下所示

a    b    c
2014  1    1
2015  1    2
2016  1    3
2017  0    1
2018  0    2
2019  -1   1
2020  -1   2
2021  -1   3
2022  1    1
2023  1    2

参考答案:

select  a,b,row_number() over( partition by b,repair_a order by a asc) as c--按照b列和[b的组首]分组,排序
from
(select  a,b,a-b_rn as repair_a--根据b列值出现的次序,修复a列值为b首次出现的a列值,称为b的[组首]from (select a,b,row_number() over( partition by b order by  a  asc ) as b_rn--按b列分组,按a列排序,得到b列各值出现的次序from t13 )tmp1
)tmp2--注意,如果不同的b列值,可能出现同样的组首值,但组首值需要和a列值 一并参与分组,故并不影响排序。
order by a asc;

十四、业务逻辑的分类与抽象--时效

日期表d_date
表字段及内容

date_id      is_work
2017-04-13       1
2017-04-14       1
2017-04-15       0
2017-04-16       0
2017-04-17       1

工作日:周一至周五09:30-18:30

客户申请表t14
表字段及内容

a      b       c
1     申请   2017-04-14 18:03:00
1     通过   2017-04-17 09:43:00
2     申请   2017-04-13 17:02:00
2     通过   2017-04-15 09:42:00

问题一:计算上表中从申请到通过占用的工作时长

输出结果如下所示

a         d
1        0.67h
2       10.67h

参考答案:

select a,round(sum(diff)/3600,2) as d
from (select a,apply_time,pass_time,dates,rn,ct,is_work,case when is_work=1 and rn=1 then unix_timestamp(concat(dates,' 18:30:00'),'yyyy-MM-dd HH:mm:ss')-unix_timestamp(apply_time,'yyyy-MM-dd HH:mm:ss')when is_work=0 then 0when is_work=1 and rn=ct then unix_timestamp(pass_time,'yyyy-MM-dd HH:mm:ss')-unix_timestamp(concat(dates,' 09:30:00'),'yyyy-MM-dd HH:mm:ss')when is_work=1 and rn!=ct then 9*3600end difffrom (select a,apply_time,pass_time,time_diff,day_diff,rn,ct,date_add(start,rn-1) datesfrom (select a,apply_time,pass_time,time_diff,day_diff,strs,start,row_number() over(partition by a) as rn,count(*) over(partition by a) as ctfrom (select a,apply_time,pass_time,time_diff,day_diff,substr(repeat(concat(substr(apply_time,1,10),','),day_diff+1),1,11*(day_diff+1)-1) strsfrom (select a,apply_time,pass_time,unix_timestamp(pass_time,'yyyy-MM-dd HH:mm:ss')-unix_timestamp(apply_time,'yyyy-MM-dd HH:mm:ss') time_diff,datediff(substr(pass_time,1,10),substr(apply_time,1,10)) day_difffrom (select a,max(case when b='申请' then c end) apply_time,max(case when b='通过' then c end) pass_timefrom t14group by a) tmp1) tmp2) tmp3 lateral view explode(split(strs,",")) t as start) tmp4) tmp5join d_date on tmp5.dates = d_date.date_id
) tmp6
group by a;

十五、时间序列--进度及剩余

表名t15
表字段及内容

date_id      is_work
2017-07-30      0
2017-07-31      1
2017-08-01      1
2017-08-02      1
2017-08-03      1
2017-08-04      1
2017-08-05      0
2017-08-06      0
2017-08-07      1

问题一:求每天的累计周工作日,剩余周工作日

输出结果如下所示

date_id      week_to_work  week_left_work
2017-07-31      1             4
2017-08-01      2             3
2017-08-02      3             2
2017-08-03      4             1
2017-08-04      5             0
2017-08-05      5             0
2017-08-06      5             0

参考答案:
此处给出两种解法,其一:

select date_id
,case date_format(date_id,'u')when 1 then 1when 2 then 2 when 3 then 3 when 4 then 4when 5 then 5 when 6 then 5 when 7 then 5 end as week_to_work
,case date_format(date_id,'u')when 1 then 4when 2 then 3  when 3 then 2 when 4 then 1when 5 then 0 when 6 then 0 when 7 then 0 end as week_to_work
from t15

其二:

select
date_id,
week_to_work,
week_sum_work-week_to_work as week_left_work
from(selectdate_id,sum(is_work) over(partition by year,week order by date_id) as week_to_work,sum(is_work) over(partition by year,week) as week_sum_workfrom(selectdate_id,is_work,year(date_id) as year,weekofyear(date_id) as weekfrom t15) ta
) tb order by date_id;

十六、时间序列--构造日期

问题一:直接使用SQL实现一张日期维度表,包含以下字段:

date                 string               日期
d_week               string               年内第几周
weeks                int                  周几
w_start              string               周开始日
w_end                string               周结束日
d_month             int                  第几月
m_start             string               月开始日
m_end               string               月结束日
d_quarter            int                    第几季
q_start             string               季开始日
q_end               string               季结束日
d_year               int                    年份
y_start             string               年开始日
y_end               string               年结束日

参考答案

drop table if exists dim_date;
create table if not exists dim_date(`date` string comment '日期',d_week string comment '年内第几周',weeks string comment '周几',w_start string comment '周开始日',w_end string comment '周结束日',d_month string comment '第几月',m_start string comment '月开始日',m_end string comment '月结束日',d_quarter int comment '第几季',q_start string comment '季开始日',q_end string comment '季结束日',d_year int comment '年份',y_start string comment '年开始日',y_end string comment '年结束日'
);
--自然月: 指每月的1号到那个月的月底,它是按照阳历来计算的。就是从每月1号到月底,不管这个月有30天,31天,29天或者28天,都算是一个自然月。insert overwrite table dim_date
select `date`, d_week --年内第几周, case weekidwhen 0 then '周日'when 1 then '周一'when 2 then '周二'when 3 then '周三'when 4 then '周四'when 5 then '周五'when 6 then '周六'end  as weeks -- 周, date_add(next_day(`date`,'MO'),-7) as w_start --周一, date_add(next_day(`date`,'MO'),-1) as w_end   -- 周日_end-- 月份日期, concat('第', monthid, '月')  as d_month, m_start, m_end-- 季节, quarterid as d_quart, concat(d_year, '-', substr(concat('0', (quarterid - 1) * 3 + 1), -2), '-01') as q_start --季开始日, date_sub(concat(d_year, '-', substr(concat('0', (quarterid) * 3 + 1), -2), '-01'), 1) as q_end   --季结束日-- 年, d_year, y_start, y_endfrom (select `date`, pmod(datediff(`date`, '2012-01-01'), 7)                  as weekid    --获取周几, cast(substr(`date`, 6, 2) as int)                        as monthid   --获取月份, casewhen cast(substr(`date`, 6, 2) as int) <= 3 then 1when cast(substr(`date`, 6, 2) as int) <= 6 then 2when cast(substr(`date`, 6, 2) as int) <= 9 then 3when cast(substr(`date`, 6, 2) as int) <= 12 then 4end                                                       as quarterid --获取季节 可以直接使用 quarter(`date`), substr(`date`, 1, 4)                                     as d_year    -- 获取年份, trunc(`date`, 'YYYY')                                    as y_start   --年开始日, date_sub(trunc(add_months(`date`, 12), 'YYYY'), 1) as y_end     --年结束日, date_sub(`date`, dayofmonth(`date`) - 1)                 as m_start   --当月第一天, last_day(date_sub(`date`, dayofmonth(`date`) - 1))          m_end     --当月最后一天, weekofyear(`date`)                                       as d_week    --年内第几周from (-- '2021-04-01'是开始日期, '2022-03-31'是截止日期select date_add('2021-04-01', t0.pos) as `date`from (select posexplode(split(repeat('o', datediff(from_unixtime(unix_timestamp('2022-03-31', 'yyyy-mm-dd'),'yyyy-mm-dd'),'2021-04-01')), 'o'))) t0) t1) t2;

十七、时间序列--构造累积日期

表名t17
表字段及内容

date_id
2017-08-01
2017-08-02
2017-08-03

问题一:每一日期,都扩展成月初至当天

输出结果如下所示

date_id    date_to_day
2017-08-01  2017-08-01
2017-08-02  2017-08-01
2017-08-02  2017-08-02
2017-08-03  2017-08-01
2017-08-03  2017-08-02
2017-08-03  2017-08-03

这种累积相关的表,常做桥接表。

参考答案:

selectdate_id,date_add(date_start_id,pos) as date_to_day
from
(selectdate_id,date_sub(date_id,dayofmonth(date_id)-1) as date_start_idfrom t17
) m  lateral view
posexplode(split(space(datediff(from_unixtime(unix_timestamp(date_id,'yyyy-MM-dd')),from_unixtime(unix_timestamp(date_start_id,'yyyy-MM-dd')))), '')) t as pos, val;

十八、时间序列--构造连续日期

表名t18
表字段及内容

a             b         c
101        2018-01-01     10
101        2018-01-03     20
101        2018-01-06     40
102        2018-01-02     20
102        2018-01-04     30
102        2018-01-07     60

问题一:构造连续日期

问题描述:将表中数据的b字段扩充至范围[2018-01-01, 2018-01-07],并累积对c求和。
b字段的值是较稀疏的。

输出结果如下所示

a             b          c      d
101        2018-01-01     10     10
101        2018-01-02      0     10
101        2018-01-03     20     30
101        2018-01-04      0     30
101        2018-01-05      0     30
101        2018-01-06     40     70
101        2018-01-07      0     70
102        2018-01-01      0      0
102        2018-01-02     20     20
102        2018-01-03      0     20
102        2018-01-04     30     50
102        2018-01-05      0     50
102        2018-01-06      0     50
102        2018-01-07     60    110

参考答案:

selecta,b,c,sum(c) over(partition by a order by b) as d
from
(selectt1.a,t1.b,casewhen t18.b is not null then t18.celse 0end as cfrom(selecta,date_add(s,pos) as bfrom(selecta, '2018-01-01' as s, '2018-01-07' as rfrom (select a from t18 group by a) ta) m  lateral view posexplode(split(space(datediff(from_unixtime(unix_timestamp(r,'yyyy-MM-dd')),from_unixtime(unix_timestamp(s,'yyyy-MM-dd')))), '')) t as pos, val) t1left join t18on  t1.a = t18.a and t1.b = t18.b
) ts;

十九、时间序列--取多个字段最新的值

表名t19
表字段及内容

date_id   a   b    c
2014     AB  12    bc
2015         23
2016               d
2017     BC

问题一:如何一并取出最新日期

输出结果如下所示

date_a   a    date_b    b    date_c   c
2017    BC    2015     23    2016    d

参考答案:
此处给出三种解法,其一:

SELECT  max(CASE WHEN rn_a = 1 THEN date_id else 0 END) AS date_a,max(CASE WHEN rn_a = 1 THEN a else null END) AS a,max(CASE WHEN rn_b = 1 THEN date_id else 0 END) AS date_b,max(CASE WHEN rn_b = 1 THEN b else NULL  END) AS b,max(CASE WHEN rn_c = 1 THEN date_id  else 0 END) AS date_c,max(CASE WHEN rn_c = 1 THEN c else null END) AS c
FROM    (SELECT  date_id,a,b,c--对每列上不为null的值  的 日期 进行排序,row_number()OVER( PARTITION BY 1 ORDER BY CASE WHEN a IS NULL THEN 0 ELSE date_id END DESC) AS rn_a,row_number()OVER(PARTITION BY 1 ORDER BY CASE WHEN b IS NULL THEN 0 ELSE date_id END DESC) AS rn_b,row_number()OVER(PARTITION BY 1 ORDER BY CASE WHEN c IS NULL THEN 0 ELSE date_id END DESC) AS rn_cFROM    t19) t
WHERE   t.rn_a = 1
OR      t.rn_b = 1
OR      t.rn_c = 1;

其二:

SELECT  a.date_id,a.a,b.date_id,b.b,c.date_id,c.c
FROM
(SELECT  t.date_id,t.aFROM  (SELECT  t.date_id,t.a,t.b,t.cFROM t19 t INNER JOIN    t19 t1 ON t.date_id = t1.date_id AND t.a IS NOT NULL) tORDER BY t.date_id DESCLIMIT 1
) a
LEFT JOIN
(SELECT  t.date_id,t.bFROM    (SELECT  t.date_id,t.bFROM t19 t INNER JOIN t19 t1 ON t.date_id = t1.date_id AND t.b IS NOT NULL) tORDER BY t.date_id DESCLIMIT 1
) b ON 1 = 1
LEFT JOIN
(SELECT  t.date_id,t.cFROM    (SELECT  t.date_id,t.cFROM t19 t INNER JOIN t19 t1 ON t.date_id = t1.date_id AND t.c IS NOT NULL) tORDER BY t.date_id DESCLIMIT   1
) c
ON 1 = 1;

其三:

select *
from
(select t1.date_id as date_a,t1.a from (select t1.date_id,t1.a  from t19 t1 where t1.a is not null) t1inner join (select max(t1.date_id) as date_id   from t19 t1 where t1.a is not null) t2on t1.date_id=t2.date_id
) t1
cross join
(select t1.date_b,t1.b from (select t1.date_id as date_b,t1.b  from t19 t1 where t1.b is not null) t1inner join (select max(t1.date_id) as date_id   from t19 t1 where t1.b is not null)t2on t1.date_b=t2.date_id
) t2
cross join
(select t1.date_c,t1.c from (select t1.date_id as date_c,t1.c  from t19 t1 where t1.c is not null) t1inner join (select max(t1.date_id) as date_id   from t19 t1 where t1.c is not null)t2on t1.date_c=t2.date_id
) t3;

二十、时间序列--补全数据

表名t20
表字段及内容

date_id   a   b    c
2014     AB  12    bc
2015         23
2016               d
2017     BC

问题一:如何使用最新数据补全表格

输出结果如下所示

date_id   a   b    c
2014     AB  12    bc
2015     AB  23    bc
2016     AB  23    d
2017     BC  23    d

参考答案:

select date_id, first_value(a) over(partition by aa order by date_id) as a,first_value(b) over(partition by bb order by date_id) as b,first_value(c) over(partition by cc order by date_id) as c
from
(select date_id,a,b,c,count(a) over(order by date_id) as aa,count(b) over(order by date_id) as bb,count(c) over(order by date_id) as ccfrom t20
)tmp1;

二十一、时间序列--取最新完成状态的前一个状态

表名t21
表字段及内容

date_id   a    b
2014     1    A
2015     1    B
2016     1    A
2017     1    B
2013     2    A
2014     2    B
2015     2    A
2014     3    A
2015     3    A
2016     3    B
2017     3    A

上表中B为完成状态

问题一:取最新完成状态的前一个状态

输出结果如下所示

date_id  a    b
2016     1    A
2013     2    A
2015     3    A

参考答案:
此处给出两种解法,其一:

selectt21.date_id,t21.a,t21.b
from(selectmax(date_id) date_id,afromt21whereb = 'B'group bya) t1inner join t21 on t1.date_id -1 = t21.date_id
and t1.a = t21.a;

其二:

selectnext_date_id as date_id,a,next_b as b
from(select*,min(nk) over(partition by a,b) as minbfrom(select*,row_number() over(partition by a order by date_id desc) nk,lead(date_id) over(partition by a order by date_id desc) next_date_id,lead(b) over(partition by a order by date_id desc) next_bfrom(select * from t21) t) t
) t
where minb = nk and b = 'B';

问题二:如何将完成状态的过程合并

输出结果如下所示:

a   b_merge
1   A、B、A、B
2   A、B
3   A、A、B

参考答案

selecta,collect_list(b) as b
from(select*,min(if(b = 'B',nk,null)) over(partition by a) as minbfrom(select*,row_number() over(partition by a order by date_id desc) nkfrom(select * from t21) t) t
) t
where nk >= minb
group by a;

二十二、非等值连接--范围匹配

表f是事实表,表d是匹配表,在hive中如何将匹配表中的值关联到事实表中?

表d相当于拉链过的变化维,但日期范围可能是不全的。

表f

date_id  p_id2017    C2018    B2019    A2013    C

表d

d_start    d_end    p_id   p_value2016     2018     A       12016     2018     B       22008     2009     C       42010     2015     C       3

问题一:范围匹配

输出结果如下所示

date_id  p_id   p_value2017    C      null2018    B      22019    A      null2013    C      3

**参考答案:
此处给出两种解法,其一:

select f.date_id,f.p_id,A.p_value
from f
left join
(select date_id,p_id,p_valuefrom (select f.date_id,f.p_id,d.p_valuefrom f left join d on f.p_id = d.p_idwhere f.date_id >= d.d_start and f.date_id <= d.d_end)A
)A
ON f.date_id = A.date_id;

其二:

select date_id,p_id,flag as p_value
from (select f.date_id,f.p_id,d.d_start,d.d_end,d.p_value,if(f.date_id between d.d_start and d.d_end,d.p_value,null) flag,max(d.d_end) over(partition by date_id) max_endfrom fleft join don f.p_id = d.p_id
) tmp
where d_end = max_end;

二十三、非等值连接--最近匹配

表t23_1和表t23_2通过a和b关联时,有相等的取相等的值匹配,不相等时每一个a的值在b中找差值最小的来匹配。

t23_1和t23_2为两个班的成绩单,t23_1班的每个学生成绩在t23_2班中找出成绩最接近的成绩。

表t23_1:a中无重复值

a
1
2
4
5
8
10

表t23_2:b中无重复值

b
2
3
7
11
13

问题一:单向最近匹配

输出结果如下所示
注意:b的值可能会被丢弃

a    b
1    2
2    2
4    3
5    3
5    7
8    7
10   11

参考答案

select *
from
(select ttt1.a,ttt1.b from(select tt1.a,t23_2.b,dense_rank() over(partition by tt1.a order by abs(tt1.a-t23_2.b)) as dr from (select t23_1.a from t23_1 left join t23_2 on t23_1.a=t23_2.b where t23_2.b is null) tt1 cross join t23_2) ttt1 where ttt1.dr=1 union allselect t23_1.a,t23_2.b from t23_1 inner join t23_2 on t23_1.a=t23_2.b
) result_t
order by result_t.a;

二十四、N指标--累计去重

假设表A为事件流水表,客户当天有一条记录则视为当天活跃。

表A

time_id          user_id
2018-01-01 10:00:00    001
2018-01-01 11:03:00    002
2018-01-01 13:18:00    001
2018-01-02 08:34:00    004
2018-01-02 10:08:00    002
2018-01-02 10:40:00    003
2018-01-02 14:21:00    002
2018-01-02 15:39:00    004
2018-01-03 08:34:00    005
2018-01-03 10:08:00    003
2018-01-03 10:40:00    001
2018-01-03 14:21:00    005

假设客户活跃非常,一天产生的事件记录平均达千条。

问题一:累计去重

输出结果如下所示

日期       当日活跃人数     月累计活跃人数_截至当日
date_id   user_cnt_act    user_cnt_act_month
2018-01-01      2                2
2018-01-02      3                4
2018-01-03      3                5

参考答案

SELECT  tt1.date_id,tt2.user_cnt_act,tt1.user_cnt_act_month
FROM
(   -- ④ 按照t.date_id分组求出user_cnt_act_month,得到tt1SELECT  t.date_id,COUNT(user_id) AS user_cnt_act_monthFROM(   -- ③ 表a和表b进行笛卡尔积,按照a.date_id,b.user_id分组,保证截止到当日的用户唯一,得出表t。SELECT  a.date_id,b.user_idFROM(   -- ① 按照日期分组,取出date_id字段当主表的维度字段 得出表aSELECT  from_unixtime(unix_timestamp(time_id),'yyyy-MM-dd') AS date_idFROM test.temp_tanhaidi_20211213_1GROUP BY  from_unixtime(unix_timestamp(time_id),'yyyy-MM-dd')) aINNER JOIN(   -- ② 按照date_id、user_id分组,保证每天每个用户只有一条记录,得出表bSELECT  from_unixtime(unix_timestamp(time_id),'yyyy-MM-dd') AS date_id,user_idFROM test.temp_tanhaidi_20211213_1GROUP BY  from_unixtime(unix_timestamp(time_id),'yyyy-MM-dd'),user_id) bON 1 = 1WHERE a.date_id >= b.date_idGROUP BY  a.date_id,b.user_id) tGROUP BY  t.date_id
) tt1
LEFT JOIN
(   -- ⑥ 按照date_id分组求出user_cnt_act,得到tt2SELECT  date_id,COUNT(user_id) AS user_cnt_actFROM(   -- ⑤ 按照日期分组,取出date_id字段当主表的维度字段 得出表aSELECT  from_unixtime(unix_timestamp(time_id),'yyyy-MM-dd') AS date_id,user_idFROM test.temp_tanhaidi_20211213_1GROUP BY  from_unixtime(unix_timestamp(time_id),'yyyy-MM-dd'),user_id) aGROUP BY date_id
) tt2
ON tt2.date_id = tt1.date_id

--end--

扫描下方二维码添加好友,备注【交流】
可私聊交流,也可进资源丰富学习群
更文不易,点个“在看”支持一下												

Hive SQL面试题(附答案)相关推荐

  1. 常见的HTML5面试题(附答案)

    常见的HTML5面试题(附答案) 1.HTML5有哪些新特性?移除了哪些元素? HTML5的新特性如下: 1.拖放( Drag and drop)APIl 2.语义化更好的内容标签( header.n ...

  2. JAVA 面试题附答案

    2019独角兽企业重金招聘Python工程师标准>>> JAVA 面试题附答案 声明,本人能力有限,只是列出来参考,不对之处欢迎指正. 一.JAVA基础 JAVA中的几种基本类型,各 ...

  3. 2020最新整理PHP面试题附答案

    2019最新整理PHP面试题附答案 1.什么事面向对象?主要特征是什么? 面向对象是程序的一种设计方式,它利于提高程序的重用性,使程序结构更加清晰.主要特征:封装.继承.多态. 2.SESSION 与 ...

  4. 2019最新整理JAVA面试题附答案

    2019最新整理JAVA面试题附答案 包含的模块: 本文分为十九个模块,分别是:Java 基础.容器.多线程.反射.对象拷贝.Java Web .异常.网络.设计模式.Spring/Spring MV ...

  5. 基础的VueJS面试题(附答案)

    基础的VueJS面试题(附答案) 1.什么是MVVM框架?它适用于哪些场景? MVVM框架是一个 Model-View-View Model框架,其中 ViewModel连接模型Model)和视图(V ...

  6. 常见的面向对象的面试题(附答案)

    常见的面向对象的面试题(附答案) 1.JavaScript是怎么样实现继承的?请举例说明. JavaScript通过 prototype属性实现继承,继承的属性方法是共享的,例如Chid子类继承 Pa ...

  7. 2022前端开发React面试题 附答案

    2022前端开发社招React面试题 附答案 1:讲讲什么是 JSX ? 主题: React 难度: ⭐⭐⭐ 当 Facebook 第一次发布 React 时,他们还引入了一种新的 JS 方言 JSX ...

  8. Word计算机与网络应用原题,计算机应用基础考试试题附答案

    计算机应用基础考试试题附答案 计算机应用普及到社会经济更多的领域.第三代集成电路计算机具有良好的性能价格比和可靠性,它促进了计算机的推广应用.下面是小编为大家整理的计算机应用基础考试试题附答案,欢迎参 ...

  9. 美图php面试题目,据说是雅虎的一份PHP面试题附答案

    据说是雅虎的一份PHP面试题附答案 更新时间:2009年01月07日 23:23:32   作者: 雅虎的一份PHP面试题附答案 从网上搜集到的,据说是雅虎的面试题. 1. Which of the ...

  10. 一级计算机考试模拟知识点,计算机一级MSOffice考试模拟试题附答案

    计算机一级MSOffice考试模拟试题附答案 做试题是备考2017计算机一级MSOffice考试的最好方法,下面小编为大家整理了计算机一级MSOffice考试模拟试题附答案. 选择题 1). 在微机的 ...

最新文章

  1. 嫌Terminal终端太单调?快收下这几个有趣的改造工具!
  2. C# List集合转Json字符串示例代码
  3. MediaSource 非当前窗口
  4. ML-2 机器学习算法
  5. 研发过程管理导图-第一稿(转)
  6. 【数字信号处理】傅里叶变换性质 ( 频域函数的共轭对称分解 | 序列的傅里叶变换 | 傅里叶变换的共轭对称 | 傅里叶变换的共轭反对称 )
  7. boost::mpl模块实现index_of相关的测试程序
  8. dotNET面试题汇总系列连载(1):基础语法
  9. Angular JS (2)
  10. 利用python自定义完整版迭代器
  11. 六、Python第六课——Python中的for循环及数字列表
  12. C# partial 部分类使用简单举例说明
  13. 【Python】Magician“专属”神秘的“读心术”
  14. AlexNet--CNN经典网络模型详解(pytorch实现)
  15. AI产品经理需要了解的数据知识:余弦相似度
  16. 【java】窗口控件及字符串和异常的综合应用
  17. github上有什么好的渗透测试软件?(Git_Pentesting_Toolkit)
  18. netron安装使用
  19. linux下ssd4k对齐,linux查看硬盘4K对齐方法
  20. 深度学习之 imgaug (图像增强)学习笔记

热门文章

  1. SHELL脚本学习指南--学习心得20110924
  2. 百度硬盘搜索SDK接口说明(摘自百度)
  3. BUUCTF笔记之Misc系列部分WriteUp(一)
  4. 秒杀抢购软件,支持淘宝/天猫、京东、拼多多和苏宁易购茅台抢购、华为/小米手机抢购,源码技术交流
  5. uos专业版与个人版区别_win+Android /wps办公软件官方专业版,内附激活码!
  6. gradle排除jar依赖
  7. 计算机读研云计算,快来看看2022云计算考研专业有哪些?
  8. VS2013 ConsoleApplication1.exe”(Win32):无法查找或打开 PDB 文件。
  9. 基因检测报告都用了哪些数据库?
  10. 焊接工时简便计算工具_2020年新版机械加工工时费用计算(17页)-原创力文档...