MYSql分组topN

Employee 表包含所有员工信息,每个员工有其对应的工号 Id,姓名 Name,工资 Salary 和部门编号 DepartmentId 。
​
+----+-------+--------+--------------+
| Id | Name  | Salary | DepartmentId |
+----+-------+--------+--------------+
| 1  | Joe   | 85000  | 1            |
| 2  | Henry | 80000  | 2            |
| 3  | Sam   | 60000  | 2            |
| 4  | Max   | 90000  | 1            |
| 5  | Janet | 69000  | 1            |
| 6  | Randy | 85000  | 1            |
| 7  | Will  | 70000  | 1            |
+----+-------+--------+--------------+
Department 表包含公司所有部门的信息。
​
+----+----------+
| Id | Name     |
+----+----------+
| 1  | IT       |
| 2  | Sales    |
+----+----------+
编写一个 SQL 查询,找出每个部门获得前三高工资的所有员工。例如,根据上述给定的表,查询结果应返回:
​
+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT         | Max      | 90000  |
| IT         | Randy    | 85000  |
| IT         | Joe      | 85000  |
| IT         | Will     | 70000  |
| Sales      | Henry    | 80000  |
| Sales      | Sam      | 60000  |
+------------+----------+--------+
解释:
​
IT 部门中,Max 获得了最高的工资,Randy 和 Joe 都拿到了第二高的工资,Will 的工资排第三。销售部门(Sales)只有两名员工,Henry 的工资最高,Sam 的工资排第二。
​
​
​
解析:
SELECT D.Name, E1.Name, E1.Salary
FROM Employee AS E1
JOIN Department AS D
ON E1.DepartmentId = D.Id
WHERE 3> (
SELECT COUNT(DISTINCT E2.`Salary`)
FROM Employee E2
WHERE E2.Salary>E1.`Salary`
AND E1.`DepartmentId`=E2.`DepartmentId`
)
ORDER BY D.`Name`,E1.`Salary` DESC;
​
不妨假设e1=e2=[6,5,4,3],则子查询的过程如下:
1、e1.Salary=3;则e2.Salary可以取4、5、6;COUNT(DISTINCT e2.Salary)=3
2、e1.Salary=4;则e2.Salary可以取5、6;COUNT(DISTINCT e2.Salary)=2
3、e1.Salary=5;则e2.Salary可以取6;COUNT(DISTINCT e2.Salary)=1
4、e1.Salary=6;则e2.Salary无法取值;COUNT(DISTINCT e2.Salary)=0
则要令COUNT(DISTINCT e2.Salary) < 3 的情况有上述的4、3、2.
也即是说,这等价于取e1.Salary最大的三个值。
​
​

三种方式处理sql的TOPN问题

1、自连接
SELECT * FROM aaa a1
WHERE 3>
(SELECT COUNT(*) FROM aaa a2
WHERE a2.`num`>a1.num
AND a1.`sex`=a2.`sex`
)
ORDER BY a1.sex, a1.num DESC
​
//解析 表内连接,在字段相同的情况下(AND a1.`sex`=a2.`sex`) 比较 数据处于表中的位置,然后(WHERE 3>)来确定要取值的N名
​
2 in
select * from stu where score in
(select max(score) ms from stu  group by subject)
​
//解析 先求出字段最大值的值,然后 字段 in ()
//注意 方法只能查最大值 若有最大值重复,可以同时求出
​
3 union all
(select * from stu where subject ='math' order by score desc limit 1)
union all
(select * from stu where subject ='english' order by score desc limit 1)")
​
​
//解析 分别在每个字段中求出最大值然后union all
//注意 该方法只能如果有重复值则结果出错建议用第一种
​

自连接排序

编写一个 SQL 查询来实现分数排名。
​
如果两个分数相同,则两个分数排名(Rank)相同。请注意,平分后的下一个名次应该是下一个连续的整数值。换句话说,名次之间不应该有“间隔”。
​
+----+-------+
| Id | Score |
+----+-------+
| 1  | 3.50  |
| 2  | 3.65  |
| 3  | 4.00  |
| 4  | 3.85  |
| 5  | 4.00  |
| 6  | 3.65  |
+----+-------+
例如,根据上述给定的 Scores 表,你的查询应该返回(按分数从高到低排列):
​
+-------+------+
| Score | Rank |
+-------+------+
| 4.00  | 1    |
| 4.00  | 1    |
| 3.85  | 2    |
| 3.65  | 3    |
| 3.65  | 3    |
| 3.50  | 4    |
+-------+------+
重要提示:对于 MySQL 解决方案,如果要转义用作列名的保留字,可以在关键字之前和之后使用撇号。例如 `Rank`
​
select s1.Score,count(distinct(s2.score)) Rank
from
Scores s1,Scores s2
where
s1.score<=s2.score
group by s1.Id
order by Rank
​
例如数据有 1,2,3,3,4,5
s1.score<=s2.score s1=1时候 s2.score= 1,2,3,3,4,5 rank=6 去重=5
s1.score<=s2.score s1=2时候 s2.score= 2,3,3,4,5 rank=6 去重=4
s1.score<=s2.score s1=3时候 s2.score= 3,3,4,5 rank=6 去重=3
s1.score<=s2.score s1=4时候 s2.score= 4,5 rank=6 去重=2
s1.score<=s2.score s1=5时候 s2.score= 5 rank=6 去重=1
​
-- 19、按各科成绩进行排序,并显示排名
SELECT s1.c_id,s1.s_id,s1.s_score, COUNT(DISTINCT(s2.s_score)) Rank
FROM
Score s1,Score s2
WHERE
s1.c_id=s2.c_id AND
s1.s_score<s2.s_score
GROUP BY s1.c_id,s1.s_id,s1.s_score
ORDER BY s1.c_id,Rank

窗口函数

-- sum()
selectuid,month,amount,sum(amount) over(partition by uidorder bymonth rows between unbounded precedingand current row) accumulate--表示从当前行加到最前面一行(
from(selectuid,month,sum(amount) amountfromt_accumulategroup byuid,month) tmp
--ntile  将数据分为n份
select a.user_id,a.sum_pay_amount,a.level
from(select user_id,sum(pay_amount) as sum_pay_amount,ntile(5) over(order by sum(pay_amount) desc) as levelfrom user_tradegroup by user_id
) a
where a.level = 1
​
--提拉函数LAG()LEAD()
编写一个 SQL 查询,查找所有至少连续出现三次的数字。
+----+-----+
| Id | Num |
+----+-----+
| 1  |  1  |
| 2  |  1  |
| 3  |  1  |
| 4  |  2  |
| 5  |  1  |
| 6  |  2  |
| 7  |  2  |
+----+-----+
------------------------------------------
select
distinct Num as count
from (select Id,Num,LAG(Num,1)OVER(order by Id) last_1,LAG(Num,2)OVER(order by Id) last_2from logs) as tmp
where
tmp.num=tmp.last_1
and
tmp.last_1=tmp.last_2

行转列

explode()函数,行转列函数
假如有以下数据
1,zhangsan,化学:物理:数学:语文
2,lisi,化学:数学:生物:生理:卫生
3,wangwu,化学:语文:英语:体育:生物create table t_stu_subject(id int,name string,subjects array<string>)
row format delimited fields terminated by ','
collection items terminated by ":";load data local inpath '/root/stu_sub.dat' into table t_stu_subject;select explode(subjects) from t_stu_subject;
+------+--+
| col  |
+------+--+
| 化学   |
| 物理   |
| 数学   |
| 语文   |
| 化学   |
| 数学   |
| 生物   |
| 生理   |
| 卫生   |
| 化学   |
| 语文   |
| 英语   |
| 体育   |
| 生物   |
+------+--+select distinct tmp.subs from (select explode(subjects) subs from t_stu_subject) tmp;+-----------+--+
| tmp.subs  |
+-----------+--+
| 体育        |
| 化学        |
| 卫生        |
| 数学        |
| 物理        |
| 生物        |
| 生理        |
| 英语        |
| 语文        |
+-----------+--+------------------------------------------------------------------------------------------------------------
1,zhangsan,化学:物理:数学:语文
2,lisi,化学:数学:生物:生理:卫生
3,wangwu,化学:语文:英语:体育:生物lateral view函数  ##横向连接
select id,name,sub from t_stu_subject lateral view explode(subjects) tmp as sub
+-----+-----------+----------+--+
| id  |   name    | tmp.sub  |
+-----+-----------+----------+--+
| 1   | zhangsan  | 化学       |
| 1   | zhangsan  | 物理       |
| 1   | zhangsan  | 数学       |
| 1   | zhangsan  | 语文       |
| 2   | lisi      | 化学       |
| 2   | lisi      | 数学       |
| 2   | lisi      | 生物       |
| 2   | lisi      | 生理       |
| 2   | lisi      | 卫生       |
| 3   | wangwu    | 化学       |
| 3   | wangwu    | 语文       |
| 3   | wangwu    | 英语       |
| 3   | wangwu    | 体育       |
| 3   | wangwu    | 生物       |
+-----+-----------+----------+--+炸map
select
id,name,key,value
from t_user lateral view explode(family) tmp as key,value=========================================================+-----+-----------+----------+--------+
| name       |   math    |  english    |
+------------+-----------+----------+--+
| zhansgan   |     32    |    88       |
| lisi       |     88    |    93       |
+------------+-----------+----------+--+
转为
zhangsan math 32
zhangsan english 88
lisi math 88
lisi english 93sql
selecttest_stuinfo.name,a.item,a.score
fromtest_stuinfo lateral view explode( str_to_map(concat('math=', math, '&english=', english), '&', '=') --{"math:21","english:90"}) a as item, score;
selectmovie,category_name
frommovie_info lateral view explode(category) table_tmp as category_name;

列转行

--case whenselect t.sname,
max(case when t.cname="语文" then t.score end ) `语文`,
max(case when t.cname="数学" then t.score end ) `数学`,
max(case when t.cname="英语" then t.score end ) `英语`
from
(select s.s_name sname,sc.s_score score,c.c_name cname from
ods_score sc,ods_student s,ods_course c
where
s.s_id=sc.s_id
and
sc.c_id = c.c_id) t
group by t.sname使用max和group by -- 
col_lie.user_id    col_lie.order_id
104399    1715131
104399    2105395
104399    1758844
104399    981085
104399    2444143
104399    1458638
104399    968412
104400    1609001
104400    2986088
104400    1795054
-- 把相同user_id的order_id按照逗号转为一行
select user_id,
concat_ws(',',collect_list(order_id)) as order_value
from col_lie
group by user_id
limit 10

NVL函数

nvl(1,2) 1 第一个函数不为null 则取1 否则2

get_json_object

get_json_object(line,'$.mid') mid,

日期处理函数

date_format("2018-06-14",'yyyy-MM')转换格式

date_format("2018-06-14",'%Y-%m')转换格式

date_add('2018-10-29','-1)前一天

date_add('2018-10-29',1)后一天

next_day('2018-10-29','MO')下一个周一

date_add(next_day('2018-10-29','MO'),-7)本周一

last_day('2018-10-29')当月最后一天

date_format(regexp_replace('2020/12/11','/','-'),'yyyy-MM' ---->2020-12

datediff(date1,date2) 日期相减,date1

如果想把 20180123 转换成 2018-01-23,可以使用:select from_unixtime(unix_timestamp('${p_date}','yyyymmdd'),'yyyy-mm-dd') from dual lim

时间戳转成日期

select distinct from_unixtime(1441565203,‘yyyy/MM/dd HH:mm:ss’) from test_date;

日期转成时间戳

select distinct unix_timestamp(‘20111207 13:01:03’) from test_date; // 默认格式为“yyyy-MM-dd HH:mm:ss“

select distinct unix_timestamp(‘20111207 13:01:03’,‘yyyyMMdd HH:mm:ss’) from test_date;

collect函数

Hive笔记之collect_list/collect_set/concat_ws/concat函数(列转行)create table t_visit_video (username string,video_name string
) partitioned by (day string)
row format delimited fields terminated by ',';vi  /home/hadoop/t_visit_video.txt 张三,大唐双龙传
李四,天下无贼
张三,神探狄仁杰
李四,霸王别姬
李四,霸王别姬
王五,机器人总动员
王五,放牛班的春天
王五,盗梦空间load data local inpath '/home/hadoop/t_visit_video.txt' into table t_visit_video partition (day='20200508');hive (felix)> select * from t_visit_video;
OK
张三    大唐双龙传      20200508
李四    天下无贼        20200508
张三    神探狄仁杰      20200508
李四    霸王别姬        20200508
李四    霸王别姬        20200508
王五    机器人总动员    20200508
王五    放牛班的春天    20200508
王五    盗梦空间        20200508
Time taken: 1.445 seconds, Fetched: 8 row(s)按用户分组,取出每个用户每天看过的所有视频的名字:
hive (felix)> select username, collect_list(video_name) from t_visit_video group by username;
张三    ["大唐双龙传","神探狄仁杰"]
李四    ["天下无贼","霸王别姬","霸王别姬"]
王五    ["机器人总动员","放牛班的春天","盗梦空间"]
Time taken: 26.414 seconds, Fetched: 3 row(s)但是上面的查询结果有点问题,因为霸王别姬实在太好看了,所以李四这家伙看了两遍,这直接就导致得到的观看过视频列表有重复的,所以应该增加去重,使用collect_set,其与collect_list的区别就是会去重:
hive (felix)> select username, collect_set(video_name) from t_visit_video group by username;
OK
张三    ["神探狄仁杰","大唐双龙传"]
李四    ["霸王别姬","天下无贼"]
王五    ["盗梦空间","放牛班的春天","机器人总动员"]
Time taken: 24.167 seconds, Fetched: 3 row(s)李四的观看记录中霸王别姬只出现了一次,实现了去重效果。--实现了linux的rownum功能hive (felix)> select username,video_name,day,row_number() over ()  rn from t_visit_video;
王五    盗梦空间        20200508        1
王五    放牛班的春天    20200508        2
王五    机器人总动员    20200508        3
李四    霸王别姬        20200508        4
李四    霸王别姬        20200508        5
张三    神探狄仁杰      20200508        6
李四    天下无贼        20200508        7
张三    大唐双龙传      20200508        8突破group by限制
还可以利用collect来突破group by的限制,Hive中在group by查询的时候要求出现在select后面的列都必须是出现在group by后面的,即select列必须是作为分组依据的列,但是有的时候我们想根据A进行分组然后随便取出每个分组中的一个B,代入到这个实验中就是按照用户进行分组,然后随便拿出一个他看过的视频名称即可:hive (felix)> select username, collect_list(video_name)[0] from t_visit_video group by username;
张三    神探狄仁杰
李四    霸王别姬
王五    盗梦空间
Time taken: 23.027 seconds, Fetched: 3 row(s)video_name不是分组列,依然能够取出这列中的数据。concat、concat_ws 函数
1)concat只是将各个字符进行拼接
2)concat_ws是将各个字符串 通过一个指定的拼接符进行拼接,相当于Oracle的wm_concat和listagg 从数据库里取N个字段,然后组合到一起用“,”分割显示,起初想到用CONCAT()来处理,好是麻烦,没想到在手册里居然有提到CONCAT_WS(),非常好用。
它是一个特殊形式的 CONCAT(),第一个参数剩余参数间的分隔符,分隔符可以是与剩余参数一样的字符串,如果分隔符是 NULL,返回值也将为 NULL,这个函数会跳过分隔符参数后的任何 NULL 和空字符串,分隔符将被加到被连接的字符串之间。
hive (felix)> SELECT CONCAT_WS(",","First name","Second name","Last Name");
OK
First name,Second name,Last Name
Time taken: 0.055 seconds, Fetched: 1 row(s)hive (felix)> SELECT CONCAT(",","First name","Second name","Last Name");
OK
,First nameSecond nameLast Name
Time taken: 0.044 seconds, Fetched: 1 row(s)hive (felix)> SELECT CONCAT_WS(",","First name",NULL,"Last Name");
OK
First name,Last Name
Time taken: 0.051 seconds, Fetched: 1 row(s)

collect_set 实例

-- 现在要生产订单号要关联出库单号,一个生产订单号可能有多个出库单号,如果join就会造成数据膨胀的问题
select * from
(select * from 生产订单)a
left join
(selectscddh,collect_set(out_general_code) out_general_code from 出库单表group by scddh -- 只用group一个就可以了)bon a.scddh=b.scddh

str_to_map

使用两个分隔符转为键值对 第一个分隔符是键值对之间的分隔符,第二个分隔符是KV之间的分隔符例:
select str_to_map('name:wangwenjie&sex:male','&',':')
结果: {"name":"wangwenjie","sex":"male"}也可以通过K获取V
select str_to_map('name:wangwenjie&sex:male','&',':')['name']
结果:wangwenjie

reflect函数

-- hive新特性
--reflect函数可以支持在sql中调用java中的自带函数,秒杀一切udf函数。
例如一张表数据
1,2
2,3
3,4
4,5
5,6
sql:select reflect("java.lang.Math","max",column1,column2) from table
--就是调用java的math中的max方法
结果:2,3,4,5,6--使用apache commons中的函数,commons下的jar已经包含在hadoop的classpath中,所以可以直接使用。
select reflect("org.apache.commons.lang.math.NumberUtils","isNumber","123") from dual;
结果:true

regexp_replace

-- 把INITIAL_STRING中与PATTERN相匹配的子串替换为REPLACEMENT
regexp_replace(string INITIAL_STRING, string PATTERN, string REPLACEMENT)ex:select regexp_replace('\n123\n','\n','456')
result:456123456ex:select regexp_replace('\n123\n','\n|2','abc')
result:abc1abc3abc用\\s替换空格,(最好) 用\\n替换\n,
hive> select regexp_replace('\n12  3   \n','\n|2|\s','abc') ;
abc1abc  3   abchive> select regexp_replace('\n12  3   \n','\n|2|\\s','abc') ;
abc1abcabcabc3abcabcabcabchive> select regexp_replace('\n12  3   \n','\\n|2|\\s','abc') ;
abc1abcabcabc3abcabcabcabc

explode和lateral view

-- 行转列
数据
100,200,300
400,500,600
sql:
select explode(split(num,',')) from table
result:
100,
200,
300,
400,
500,
600-- 联合lateral view
数据:
pageAds表
string pageid   Array<int> adid_list
"front_page"  [1, 2, 3]
"contact_page"    [3, 4, 5]--要统计所有广告ID在所有页面中出现的次数。
首先分拆广告ID:
select pageid,adid from pageAds lateral view explode(adid_list) adtable as adid结果
string pageid   int adid
"front_page"  1
"front_page"  2
"front_page"  3
"contact_page"    3
"contact_page"    4
"contact_page"    5SELECT adid, count(1) FROM pageAds LATERAL VIEW explode(adid_list) adTable AS adid
GROUP BY adid;执行结果如下:int adid    count(1)
1   1
2   1
3   2
4   1
5   1多个lateral view语句:
一个FROM语句后可以跟多个lateral view语句,后面的lateral view语句能够引用它前面的所有表和列名。
以下面的表为例:
Array<int> col1       Array<string> col2
[1, 2]  [a", "b", "c"]
[3, 4]  [d", "e", "f"]
SELECT myCol1, col2 FROM baseTableLATERAL VIEW explode(col1) myTable1 AS myCol1;
的执行结果为:intmycol1 Array<string> col2
1   [a", "b", "c"]
2   [a", "b", "c"]
3   [d", "e", "f"]
4   [d", "e", "f"]加上一个lateral view:SELECT myCol1, myCol2 FROM baseTableLATERAL VIEW explode(col1) myTable1 AS myCol1LATERAL VIEW explode(col2) myTable2 AS myCol2;它的执行结果为:intmyCol1  string myCol2
1   "a"
1   "b"
1   "c"
2   "a"
2   "b"
2   "c"
3   "d"
3   "e"
3   "f"
4   "d"
4   "e"
4   "f"注意上面语句中,两个lateral view按照出现的次序被执行。

笛卡尔积应用

id course
1,a
1,b
1,c
1,e
2,a
2,c
2,d
2,f
3,a
3,b
3,c
3,e
编写Hive的HQL语句来实现以下结果:
表中的1表示选修,表中的0表示未选修id    a    b    c    d    e    f
1    1    1    1    0    1    0
2    1    0    1    1    0    1
3    1    1    1    0    1    0
--------------------------------------------------------------
首先获取所有的课程
select collect_set(trim(course)) id_course from stuinfo group by id;--["a","b","c","d","e","f"]
--------------------------------------------------------------
求每一个学员的课程
select
id,collect_set(trim(course)) id_course
from interview04
group by id;
--------------------------------------------------------------
将所有课程和所选课程join形成笛卡尔积
select
b.id id,b.id_course id_course,a.courses courses
from
(select collect_set(trim(course))courses from stuinfo
) a join
(select
id,collect_set(trim(course)) id_course
from interview04
group by id
) b;
id      id_course               courses
1       ["a","b","c","e"]       ["a","b","c","e","d","f"]
2       ["a","c","d","f"]       ["a","b","c","e","d","f"]
3       ["a","b","c","e"]       ["a","b","c","e","d","f"]-------------------------------------------------------------
最终判断coureses是否包含学生所选课程,包含为1不包含为0
select
id,
if(array_contains(id_course,courses[0]),1,0) a, --如果id_course(学生所选课程)包含courses[0](全部课程[0])
if(array_contains(id_course,courses[1]),1,0) b,
if(array_contains(id_course,courses[2]),1,0) c,
if(array_contains(id_course,courses[3]),1,0) e,
if(array_contains(id_course,courses[4]),1,0) d,
if(array_contains(id_course,courses[5]),1,0) f
from
(select
b.id id,b.id_course id_course,a.courses from stuinfo
from
(select collect_set(trim(course))courses from stuinfo ) a --所有的课程
join
(select collect_set(trim(course))id_course from stuinfo group by id)b )c--学生所选课程

连续登录

select'2020-06-22',concat(date_add('2020-06-22', -6), '|2020-06-22'),count(*)
from(selectmid_idfrom(-- 查询差日期为同一天的结果大于等于3selectmid_id,count(*) cntsfrom( --日期减去排序,连续登陆则差相同selectmid_id,date_sub(dt, rk) date_diff from(-- 先根据ID给对应的日期排序selectmid_id,dt,row_number() over(partition by mid_id order by dt) rkfromdws_uv_detail_daycountwheredt >= date_add('2020-06-22', -6)and dt <= '2020-06-22') t1) t2group bymid_id,date_diffhavingcnts >= 3) t3group bymid_id) t4

树状结构两种情况

-- 层级相同 有最大层级 获取所有的层级
WITH t AS ( SELECT
parent_id
,id
,category_name,level
FROM
s_wxapp_swp_product_physic_category where ds=20210512 ) SELECT a.id lv1
,a.category_name lv1_name
,b.id lv2
,b.category_name lv2_name
,c.id lv3
,c.category_name lv3_name
,d.id lv4
,d.category_name lv4_name
FROM (
SELECT
parent_id
,id
,category_name
FROM
t
WHERE level=4
) a
LEFT JOIN t b
ON a.parent_id = b.id LEFT
JOIN t c
ON b.parent_id = c.id
LEFT JOIN t d
ON c.parent_id = d.id
;-- 每个层级不固定,最大层级不确定只能最大循环,且只取最大最小
SELECTIF(g.pid IS NULL,IF(f.pid IS NULL,IF(e.pid IS NULL,IF(d.pid IS NULL,IF(c.pid IS NULL, b.pid, c.pid),d.pid),e.pid),f.pid),g.pid) product,a.bid
FROM(SELECTbidFROMdiguiWHEREbid NOT IN (SELECTpidFROMdigui)) aLEFT JOIN digui b ON a.bid = b.bidLEFT JOIN digui c ON b.pid = c.bidLEFT JOIN digui d ON c.pid = d.bidLEFT JOIN digui e ON d.pid = e.bidLEFT JOIN digui f ON e.pid = f.bidLEFT JOIN digui g ON g.pid = f.bid
GROUP BYproduct,bid

数据倾斜

文章链接

https://blog.csdn.net/u010670689/article/details/42920917?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162495266916780262577659%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=162495266916780262577659&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-1-42920917.pc_search_result_control_group&utm_term=hive+%E6%95%B0%E6%8D%AE%E5%80%BE%E6%96%9C%E6%80%8E%E4%B9%88%E5%A4%84%E7%90%86&spm=1018.2226.3001.4187https://blog.csdn.net/strongyoung88/article/details/100594082?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162495266816780357210641%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=162495266816780357210641&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-3-100594082.pc_search_result_control_group&utm_term=hive+%E6%95%B0%E6%8D%AE%E5%80%BE%E6%96%9C%E6%80%8E%E4%B9%88%E5%A4%84%E7%90%86&spm=1018.2226.3001.4187
3.1空值产生的数据倾斜
场景:如日志中,常会有信息丢失的问题,比如日志中的 user_id,如果取其中的 user_id 和 用户表中的user_id 关联,会碰到数据倾斜的问题。解决方法1: user_id为空的不参与关联(红色字体为修改后)select * from log ajoin users bon a.user_id is not nulland a.user_id = b.user_idunion allselect * from log awhere a.user_id is null;解决方法2 :赋与空值分新的key值select *from log aleft outer join users bon case when a.user_id is null then concat(‘hive’,rand() ) else a.user_id end = b.user_id;结论:方法2比方法1效率更好,不但io少了,而且作业数也少了。解决方法1中 log读取两次,jobs是2。解决方法2 job数是1 。这个优化适合无效 id (比如 -99 , ’’, null 等) 产生的倾斜问题。把空值的 key 变成一个字符串加上随机数,就能把倾斜的数据分到不同的reduce上 ,解决数据倾斜问题。3.2不同数据类型关联产生数据倾斜
场景:用户表中user_id字段为int,log表中user_id字段既有string类型也有int类型。当按照user_id进行两个表的Join操作时,默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer中。解决方法:把数字类型转换成字符串类型select * from users aleft outer join logs bon a.usr_id = cast(b.user_id as string)3.3小表不小不大,怎么用 map join 解决倾斜问题
使用 map join 解决小表(记录数少)关联大表的数据倾斜问题,这个方法使用的频率非常高,但如果小表很大,大到map join会出现bug或异常,这时就需要特别的处理。 以下例子:select * from log aleft outer join users bon a.user_id = b.user_id;users 表有 600w+ 的记录,把 users 分发到所有的 map 上也是个不小的开销,而且 map join 不支持这么大的小表。如果用普通的 join,又会碰到数据倾斜的问题。解决方法:select /*+mapjoin(x)*/* from log aleft outer join (select  /*+mapjoin(c)*/d.*from ( select distinct user_id from log ) cjoin users don c.user_id = d.user_id) xon a.user_id = b.user_id;假如,log里user_id有上百万个,这就又回到原来map join问题。所幸,每日的会员uv不会太多,有交易的会员不会太多,有点击的会员不会太多,有佣金的会员不会太多等等。所以这个方法能解决很多场景下的数据倾斜问题。4总结
使map的输出数据更均匀的分布到reduce中去,是我们的最终目标。由于Hash算法的局限性,按key Hash会或多或少的造成数据倾斜。大量经验表明数据倾斜的原因是人为的建表疏忽或业务逻辑可以规避的。在此给出较为通用的步骤:1、采样log表,哪些user_id比较倾斜,得到一个结果表tmp1。由于对计算框架来说,所有的数据过来,他都是不知道数据分布情况的,所以采样是并不可少的。2、数据的分布符合社会学统计规则,贫富不均。倾斜的key不会太多,就像一个社会的富人不多,奇特的人不多一样。所以tmp1记录数会很少。把tmp1和users做map join生成tmp2,把tmp2读到distribute file cache。这是一个map过程。3、map读入users和log,假如记录来自log,则检查user_id是否在tmp2里,如果是,输出到本地文件a,否则生成<user_id,value>的key,value对,假如记录来自member,生成<user_id,value>的key,value对,进入reduce阶段。4、最终把a文件,把Stage3 reduce阶段输出的文件合并起写到hdfs。如果确认业务需要这样倾斜的逻辑,考虑以下的优化方案:1、对于join,在判断小表不大于1G的情况下,使用map join2、对于group by或distinct,设定 hive.groupby.skewindata=true3、尽量使用上述的SQL语句调节进行优化

常用的sql知识点总结相关推荐

  1. 经典SQL语句大全、50个常用的sql语句

    50个常用的sql语句 Student(S#,Sname,Sage,Ssex) 学生表 Course(C#,Cname,T#) 课程表 SC(S#,C#,score) 成绩表 Teacher(T#,T ...

  2. 常用经典SQL语句大全完整版--详解+实例 (存)

    常用经典SQL语句大全完整版--详解+实例 转 傻豆儿的博客 http://blog.sina.com.cn/shadou2012  http://blog.sina.com.cn/s/blog_84 ...

  3. mysql 10分钟_10分钟入门mysql(含常用的sql语句,mysql常见问题及解决方案)

    开发中常用的sql语句 1,创建一个数据库并指定编码格式 drop database if exists test;create database test default character set ...

  4. WordPress 常用数据库SQL查询语句大全

    https://www.wpdaxue.com/wordpress-sql.html 在使用WordPress的过程中,我们少不了要对数据库进行修改操作,比如,更换域名.修改附件目录.批量修改文章内容 ...

  5. [转载]oracle常用经典SQL查询

    原文地址:oracle常用经典SQL查询作者:来者 oracle常用经典SQL查询 1.查看表空间的名称及大小  select t.tablespace_name, round(sum(bytes/( ...

  6. 50个常用mysql语句_50个常用的sql语句

    50个常用的sql语句 Student(S#,Sname,Sage,Ssex) 学生表 Course(C#,Cname,T#) 课程表 SC(S#,C#,score) 成绩表 Teacher(T#,T ...

  7. 【面试复习系列】常用机器学习算法知识点及其解析,面试官会考的几乎都有,欢迎补充

    图片慢慢上传,看不到图片的请点这里: 常用机器学习算法知识点及其解析,面试官会考的几乎都有 LR:logistic regression  对数几率回归/逻辑回归 sigmoid函数的作用就是用于把输 ...

  8. 常用的sql语句,sql使用大全

    常用的sql语句,sql使用大全 我工作中常用到的sql 插入 查询 更新 介绍其他的sql SQL分类 基本的sql语句 高级查询运算词 我工作中常用到的sql 下面是我工作中常用的sql,每次都是 ...

  9. 【写博客常用】sql server 登陆服务器名称是什么

    [写博客常用]sql server 登陆服务器名称是什么 说明:这是我遇到的一个问题,折磨了很久. 我在网上搜索了半天,大部分人都说是local,localhost,单独的一个点,127.0.0.1等 ...

最新文章

  1. 受用一生的高效 PyCharm 使用技巧(六)
  2. 详解SSH框架的原理和优点
  3. 这10个著名的思想实验,竟然是物理学家完成的
  4. 基于thinkphp的省略图便捷函数
  5. java数组 相同颜色距离最远的_java-数组列表并找到具有相同编号的最长子...
  6. 超级实用的设计类网址导航--一流设计导航
  7. JavaBean在jsp中的使用
  8. 题目1026:又一版 A+B
  9. AtomicInteger的CAS算法浅析
  10. Java动态生成excel模板、和动态模板数据导出
  11. 蓝桥杯每日一练----字符串逆序
  12. cs1.6一直连接服务器,CS1.6连接不上服务器解决办法
  13. vs code语言模式自动检测设置
  14. 【Android 第三方SDK】breakpad在linux下编译
  15. (哈希)Squares (p2002)
  16. python中的列表list(四):列表的遍历
  17. 马斯克回应福特CEO喊话:我已经有一辆Cybertruck了
  18. “利空”砸盘?中本聪:这个锅我不背
  19. 重磅 | 分子生物学与遗传学经典名著——《Lewin基因XII》(中译本)
  20. 关于android 栏沉浸式导航的一些事儿

热门文章

  1. Spring AOP 和 AspectJ的区别
  2. 基于java的企业门户网站
  3. 找一找mini usb 的AD封装
  4. 坚果虽好吃进食有禁忌
  5. 2019年1+X 证书 Web 前端开发中级理论考试——易错题、陌生但又会考到的题目原题+答案(文章含五套题的内容)
  6. HTTPS 和 证书
  7. 各种https证书类型有什么不同
  8. E. Product Oriented Recurrence (矩阵快速幂新模板)
  9. 如何通俗易懂地理解去中心化身份?
  10. python学习好书推荐