hive基础知识总结

  1. 分桶

1.1什么是hash值

将分桶之前,先讲一下什么是hash值(哈希值)?(比如 一个数或一个字符串的hash值,如5这个数的hash值。)

 

在这里,“5的hash值”这句话要中隐含着三个关键点:

  1. hash函数  
  2. 被hash函数处理的元素
  3. hash函数处理后的结果值

例子:5 %4 = 1  

其中

  1. “%4”就是一个hash函数   当然hash函数有各式各样的  如  md5、crc 等等hash函数
  2. “5” 就是被hash函数处理的元素
  3. “1” 就是被hash函数处理后的结果值

1.2 hash值得意义

hash一般用来数据查找。
比如有一个数组{1,2,200,300,123,444,555,666,777,222,211,22,33,4,5,33}
如何查找这个数组呢,最简单的当然是遍历数组,一个个比。但是如果这个数组很大,这样效率肯定很低。
为了更好的查找数组,我们可以把这个数组分成多个子数组,比如分成2个数组,那么可以简单的奇数一个数组,偶数一个数组,如果分成4个,那么可以根据最低端2位来,或者说v%4。
v%4这个就是hash值。对整数数据来说,取模是个很简单的hash函数。

那如果是字符串呢,常见的有crc,md5等。如md5,无论字符串多长,都计算出32字节的md5值,那么比较32字节会比比较大字符串快很多。
hash值相等,原值不一定相等。但是hash值不相等,原值必定不相等。这个特性也用来验证源码是否被修改,如发布一个程序,计算出md5值,那么使用者验证下md5值,如果不相等,则肯定被人修改过了,说不定就被人安装了木马。那么如果相等,则也有可能被修改过的,但是修改过之后md5要一样,就有点难,几乎不可能出现。

 

 

1.3 什么是分桶

简单说:就是将表中的某个字段,进行hash取值,将相同的hash值得的记录放入到同一个桶中,一个桶就是一个文件。

 

Hive以表中的某一列作为分桶的依据,桶的个数有用户设置,这里以用户表中的id字段来划分桶,划分4个桶。Hive会计算分桶列的hash值再以桶的个数取模来计算某条记录属于那个桶。

分桶的优点在于,将数据大致平均的、随机的放入多个桶中,这样方便对海量的数据做抽样调查、分析。

 

1.4 分桶操作

 

创建分桶表 

(1)创建分桶表

Create table order_buck(id int , name string ,rl string , price double)

Clustered by (id) sorted by(price) into 2 buckets      cluster 集群)

Row format delimited

fields terminated by  ‘\t’;

上述语句,定义了一个 id进行分桶,同时根据price进行排序放入2个桶里

向该表进行插入数据时,也要体现上述三个特性

Id分桶

Price排序

2个桶

下面的(2语句体现了 桶的个数 3语句体现了 price排序  id分桶

 

(2)向表中插入数据 -- 先设置变量,设置分桶为true, 设置reduce数量是分桶的数量个数

set hive.enforce.bucketing=true;

set mapreduce.job.reduces=2;

 

 

(3)向表中插入数据 -- 我们从另外一个表t_tmp查询数据放到该表中

开始往创建的分桶表插入数据  (插入数据需要是已分桶, 排序的)  注意这两点

insert into table t_order_buck                                        

select id,name,rl,price from t_order

distribute by(id)     #distribute 关键字就是 桶操作必备的   distrbute 分发)

sort by(price);

 

 

往分桶表导入数据时,一般不适用load方式,而是使用insert。。。Select方式。

    (4)分桶表和表的内容对应的文件存放在hfds的位置是:

/user/hive/warehouse/bucket_db.db/t_order_buck/00000_0

/user/hive/warehouse/bucket_db.db/t_order_buck/00001_0

00000_000001_0最终对应的表数据的文件   因为set mapreduce.job.reduces=2所以 两个 文件

 

向分桶表中插入数据方式:

    1.上面向分桶表中插入数据的关键语句是:distribute by(id) sort by(price),可见按id字段进行分桶;按price字段进行桶内排序。排序分桶字段相同

2.当排序分桶字段相同的时候也可以使用Cluster by(字段)代替上面的例子中distribute by(id) sort by(price)语句。

insert into table t_order_buck                                        

select id,name,rl,price from t_order

Cluster by(id);

             如

 

 

注意使用cluster by 就等同于分桶+排序(sort)

如果分桶和sort字段是同一个时,此时,cluster by = distribute by + sort by

可以尝试以下几种方式:

insert into table stu_buc

select id,name,score from student distribute by(id) sort by(id asc);  

 

上面这个例子的“排序”和“分桶”的字段是相同的,都是id,因此可cluster by 方式向分桶表中写入数据

insert overwrite table stu_buck
select id,name,score from student cluster by(id);

 

insert overwrite table stu_buck
select id,name,score from student cluster by(id) sort by(id); 报错,cluster  sort 不能共存

 

 

“order by” 和“sort by"的区别

 

注:1order by 会对输入做全局排序,因此只有一个reducer,会导致当输入规模较大时,需要较长的计算时间。

    2sort by不是全局排序,其在数据进入reducer前完成排序。因此,如果用sort by进行排序,并且设置mapred.reduce.tasks>1,则sort by只保证每个reducer的输出有序,不保证全局有序。

    3distribute by(字段)根据指定的字段将数据分到不同的reducer,且分发算法是hash散列。

    4Cluster by(字段) 除了具有Distribute by的功能外,还会对该字段进行排序。

    5、创建分桶表并不意味着load进数据也是分桶的,你必须先分好桶,然后再放到表中。

 

1.5 分桶的意义

分桶表的作用:最大的作用是用来提高join操作的效率;但是两者的分桶数要相同或者成倍数。

(这句话的意思:一般join操作是两个表(a表,b表),a的分桶数 == b的分桶数  ||  a的分桶数 % b的分桶 == 0

为什么可以提高join操作的效率呢?因为按照MapReduce的分区算法,是IdHashCode值模上ReduceTaskNumbers,所以一个ID会分到同一个桶中,这样合并就不用整个表遍历求笛卡尔积了,对应的桶合并就可以了。

 

分桶基本原理

就是往“分桶表”插入的记录,根据计算id的值hash值,插入到2个不同的数据源中。

 

 

把表(或者分区)组织成桶(Bucket)有两个理由:

(1)获得更高的查询处理效率。桶为表加上了额外的结构,Hive 在处理有些查询时能利用这个结构。具体而言,连接两个在(包含连接列的)相同列上划分了桶的表,可以使用 Map 端连接 (Map-side join)高效的实现。比如JOIN操作。对于JOIN操作两个表有一个相同的列,如果对这两个表都进行了桶操作。那么将保存相同列值的桶进行JOIN操作就可以,可以大大较少JOIN的数据量。

2)使取样(sampling)更高效。在处理大规模数据集时,在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,会带来很多方便。按我的理解,所谓Hive中的分桶,实际就是指的MapReduce中的分区。根据Reduce的数量,分成不同个数的文件。

 

  1. 数据类型

2.1基本数据类型

 

例子:

create table t_dataType( i1 tinyint , b boolean)

    > row format delimited

    > fields terminated by ',';

insert into t_dataType2 values(1,true);  #正常

insert into t_dataType2 values(1000,true);  #1000超过tinyint的范围

 向i1中插入超过tinyint范围的值,则该字段的值是null

 

查找当前日期

Select current_date;  #2018-06-1

Select current_timestamp;  #2018-06-17 04:53:32.25

Select unix_timestamp(); #1529182469

Select from_unixtime(unix_timestamp()); #2018-06-17 04:55:10

 

create table t_base if not exists base(

salary double,

salary1 float,

dt timestamp     #识别’2018-06-11 11:11:12’ 这样格式的字符串

)

row format delimited

fields terminated by  ', ';

2.2复杂数据类型

 

例子:

STRUCT
类似于C、C#语言,Hive中定义的struct类型也可以使用点来访问。从文件加载数据时,文件里的数据分隔符要和建表指定的一致。

建表

create table IF NOT EXISTS t_struct

(id int,info struct<name:string,hobby:string>)

ROW FORMAT DELIMITED FIELDS TERMINATED BY ','

COLLECTION ITEMS TERMINATED BY ':'

STORED AS TEXTFILE;

 

创建一个文本文件struct_data.txt

1,aa:football 

2,bb:basketball 

3,cc:sleeping  

 

导入数据

LOAD DATA LOCAL INPATH '/data/struct_data.txt' OVERWRITE INTO TABLE t_struct;

 

查询数据

select * from t_struct;

 

1 {"name":"aa","hobby":"football"}

2 {"name":"bb","hobby":"basketball"}

 

 

Select id,info.name from t_struct

 

问题:

   查询 姓名是zs 的用户id、用户名 、爱好

 

 

ARRAY
ARRAY表示一组相同数据类型的集合,下标从零开始,可以用下标访问

CREATE TABLE  t_array (id int,name array<STRING>)

ROW FORMAT DELIMITED FIELDS TERMINATED BY ','

COLLECTION ITEMS TERMINATED BY ':';

 

创建一个文本文件array_data.txt

1,cys:changyansong

2,gh:guanhang

3,cgjj:changguanjiujiu

 

导入数据

LOAD DATA LOCAL INPATH '/data/array_data.txt' OVERWRITE INTO TABLE t_array;

 

查询数据

select * from t_array;  

1 ["cys","changyansong"]

2 ["gh","guanhang"]  

 

Select id,name[0],name[1] from t_array;

 

 

问题:

数组长度大等于2 查询姓名和第一个元素


MAP
MAP是一组键值对的组合,可以通过KEY访问VALUE,键值之间同样要在创建表时指定分隔符。

create table if not exists t_map(

uname string,

mp1 Map<String,Double>    

)

row format delimited fields terminated by ' '

collection items terminated by ','

map keys terminated by ':';

 

创建一个文本文件map_data.txt

zs high:180,weight:130

li high:177,weight:120

 

加载数据

LOAD DATA LOCAL INPATH '/data/map_data.txt' OVERWRITE INTO TABLE t_map;

 

查询数据 select * from map_1;

zs      {"high":180.0,"weight":130.0}

li      {"high":177.0,"weight":120.0}  

 

Select mp1[‘hight’],mp1[‘weight’] from t_map;

 

问题:

   查询map长度大于等于2 身高大于178的用户的名字 、 升高 、体重

 

 

UINON TYPES
Hive除了支持STRUCT、ARRAY、MAP这些原生集合类型,还支持集合的组合,不支持集合里再组合多个集合。
简单示例MAP嵌套ARRAY,手动设置集合格式的数据非常麻烦,建议采用INSERT INTO SELECT 形式构造数据再插入UNION表。

创建DUAL表,插入一条记录,用于生成数据

create table dual(d string);

insert into dual values('X');

 

创建UNION表

CREATE TABLE IF NOT EXISTS t_union

( id int, info map<STRING,array<STRING>> )

ROW FORMAT DELIMITED FIELDS TERMINATED BY ','

COLLECTION ITEMS TERMINATED BY '-'

MAP KEYS TERMINATED BY ':'

 

插入数据

insert overwrite table t_union

select 1 as id,map('english',array(99,21,33)) as info from dual

union all

select 2 as id,map('english',array(44,33,76)) as info

from dual

union all

select 3 as id,map('english',array(76,88,66)) as info

from dual;

 

查询数据

select * from uniontype_1;

3 {"german":[76,88,66]}

2 {"chinese":[44,33,76]}

1 {"english":[99,21,33]}

 

select * from uniontype_1 where info['english'][2]>30;

1 {"english":[99,21,33]}

 

 

 

复杂数据类型综合练习:

若干公司中,员工的五险一金的扣款情况,以及扣款的所属片区。

 

 

 

  1. 函数

3.1内置函数

  1. 随机数函数  rand()

select rand();  //取得一个0-1的一个随机数

 

2、切分函数  split(st r ,  splitor)    #注意特殊分割符的转义

select split(5.0,"\\.")[0];

select split(rand()*100,"\\.")[0];

 

3、substr(str,x,y)  substring()    #  x表示起始位置(0/1是相同效果),y 表示获取字符串的长度

select substr(rand()*100,0,2);

select substring(rand()*100,0,2);

 

4、if(expr,truestament,falsestament)

select if(100>10,"this is true","this is false");

select if(2=1,"男","女");

select if(1=1,"男",(if(1=2,"女","不知道")));

select if(3=1,"男",(if(3=2,"女","不知道")));

5、case i when v ...else  end; case when i<expr when ..else  end;

select

case 6

when 1 then "100"

when 2 then "200"

when 3 then "300"

when 4 then "400"

else "others"

end;

 

create table if not exists cw(

flag int

);

load data local inpath '/home/flag' into table cw;

 

select

case c.flag

when 1 then "100"

when 2 then "200"

when 3 then "300"

when 4 then "400"

else "others"

end

from cw c;

 

 

select

case

when 1=c.flag then "100"

when 2=c.flag then "200"

when 3=c.flag then "300"

when 4=c.flag then "400"

else "others"

end

from cw c;

 

6、正则替换   regexp_replace(str,regex,"替换成什么样")

select regexp_replace("1.jsp",".jsp",".html");

 

7、cast : 类型转换 cast(str as double)

select 1;

select cast(1 as double);   #将1转换为  double类型

select cast("12" as int);    #将字符串12  转化为int类型

 

8、字符串连接   concat(str1,str2,...) concat_ws()

 

select "千峰" + 06 + "班级";

select concat("千峰",06,"班级");

 

#concat_ws表示在 "千峰"、"1603"和"班级"之间添加一个"|"分隔符

select concat_ws("|","千峰","1603","班级");

 

 

  1. 排名函数:

解决top n的问题

ROW_NUMBER() OVER函数的基本用法 
语法:ROW_NUMBER() OVER(PARTITION BY COLUMN ORDER BY COLUMN)
简单的说row_number()从1开始,为每一个分组记录返回一个数字,这里的ROW_NUMBER() OVER (ORDER BY xlh DESC) 是先把xlh列降序,再为降序以后的每条xlh记录返回一个序号。 
示例:              
xlh          row_num 
1700             1    
1500             2    
1085             3    
710            4   

row_number() OVER (PARTITION BY COL1 ORDERBY COL2) 表示根据COL1分组,在分组内部根据 COL2排序,而此函数计算的值就表示每组内部排序后的顺序编号(该编号在组内是连续并且唯一的) 。

 

用实际例子演示 row_number()函数的效果

示例数据:

    pId   dept   sal

1      a   10

2      a   12

3      b   13

4      b   12

5      a   14

6      a   15

7      a   13

8      b   11

9      a   16

10     b   17

11     a   14

 

执行查询语句

select pid,

Dept,

sal,

row_number()over(partition by dept order by sal )

from f_test;

 

 

查询后的输出效果:

看到row_number的效果了吧。

 

那这个row_number有什么用?

hive中一般取top n时,row_number(),函数就派上用场了,

 

如本例中求出每个部门中工资前三名的员工编号。

Select *  from

select pid,

dept,

sal,

row_number()over(partition by dept order by sal desc)  as rmp

from f_test

) as t_t_n

Where t_t_n.rmp <=3;

 

 

与row_number相近的函数还有,rank,dense_ran()那这个两个函数有什么效果呢?

在上面事例数据的基础上执行如下语句:

select id,

name,

sal,

rank()over(partition by name order by sal desc ) rp,

dense_rank() over(partition by name order by sal desc ) drp,

row_number()over(partition by name order by sal desc) rmp

from f_test

 

效果为:

从结果看出

rank() 排序相同时会重复,总数不会变  

dense_rank()排序相同时会重复,总数会减少

row_number() 会根据顺序计算

 

 

 

  1. 聚合函数:min() max() count() count(distinct ) sum() avg()

count(1):不管整行有没有值,只要出现就累计1

count(*):整行值只要有一个不为空就给累计1

count(col):col列有值就累计1

count(distinct col):col列有值并且不相同才累计1

 

 

11.其他

 

######

几乎任何数和 NULL操作都返回NULL

 

&& 和 || 不支持  使用and 和  or代替

 

size()   取数组或map数据类型的大小

round()  四舍五入取整

select length("cys");  #求字符串长度

locat(string substr ,string str)  #求子字符串substr在字符串str中的位置

trim()  #去除字符串中前后空格

parse_url()  #解析url中的各个部分   http://localhost:8880/aa/bb/1.html

hash()

 

 

 

select from_unixtime(timestamp,"yyyy-MM-dd")  #根据时间戳的值,获取相应的日期格式

select unix_timestamp();  #获取当前系统时间的时间戳值

select unix_timestamp("2017-04-19 00:00:00");  #把指定日期,转化为时间戳的值

select year("2017-04-19 00:00:00");   #根据指定日期,获取年

select month("2017-04-19 00:00:00");

select day("2017-04-19 00:00:00");

select hour("2017-04-19 00:00:00");

select weekofyear("2017-04-19 00:00:00");

 

其他函数参见 doc\Hive函数大全.pdf

 

3.2自定义函数和Transform

当Hive提供的内置函数无法满足你的业务处理需要时,此时就可以考虑使用用户自定义函数(UDF:user-defined function)。

 

3.2.1 自定义函数类别

UDF  作用于单个数据行,产生一个数据行作为输出。(数学函数,字符串函数)

UDAF(用户定义聚集函数):接收多个输入数据行,并产生一个输出数据行。(count,max)

 

3.2.2 UDF开发实例

 0、先导入相应的jar包   (位置  当天资料里面software/lib-开发)

1、先开发一个java类,继承UDF,并重载evaluate方法

Package com.qianfeng.udf;

import org.apache.hadoop.hive.ql.exec.UDF;

import org.apache.hadoop.io.Text;

 

public final class Lower extends UDF{

public Text evaluate(final Text s){

if(s==null){return null;}

return new Text(s.toString().toLowerCase());

}

}

 

2、打成jar包上传到服务器

3、将jar包添加到hive的classpath,在hive命令行下执行下面语句

hive>add JAR /home/hadoop/udf.jar;

4、创建临时函数与开发好的java class关联

Hive>create temporary function Lower as 'com.qianfeng.udf.Lower';

 

5、即可在hql中使用自定义的函数strip

Select Lower(name),age from t_test;

     

  1. 如果该函数确定不再使用时可以删除

  Drop temporary  function Lower;

 

案例:

根据生日得到年龄,使用自定义函数实现。

4.3.3 Transform实现

Hive的 TRANSFORM 关键字提供了在SQL中调用自写脚本的功能

适合实现Hive中没有的功能又不想写UDF的情况

 

有一个u_data表,其内容如下:

用户在几点看了电影,并给电影进行了评分

movieid(电影id)

rating(评分)

unixtime(时间戳)

userid(用户id)

1193

5

978300760

1

661

3

978302109

1

需求:将时间戳变为周几

 

 1.创建一个新表u_data_new

CREATE TABLE u_data_new (

  movieid INT,      

  rating INT,        

  weekday INT,  #新表字段存放“周几型日期”

  userid INT)

ROW FORMAT DELIMITED

FIELDS TERMINATED BY '\t';

 2.将phyton脚本加入hive

add FILE weekday_mapper.py;

3.使用脚本 

INSERT OVERWRITE TABLE u_data_new

SELECT

  TRANSFORM  (movieid, rating, unixtime,userid)  #要转换的字段,相当于是脚本的输入字段

  USING  'python weekday_mapper.py'   #使用脚本转换

  AS (movieid, rating, weekday,userid)  #脚本返回的值放入这些字段中

FROM u_data;

 

其中weekday_mapper.py内容如下

#!/bin/python

import sys

import datetime

 

for line in sys.stdin:    //从标准输入一行

  line = line.strip()     //得到一行的数据

  movieid, rating, unixtime,userid = line.split('\t')

  weekday = datetime.datetime.fromtimestamp(float(unixtime)).isoweekday()

  print '\t'.join([movieid, rating, str(weekday),userid])

 

使用示例2:下面的例子则是使用了shell的cat命令来处理数据

FROM invites a INSERT OVERWRITE TABLE events SELECT TRANSFORM(a.foo, a.bar) AS (oof, rab) USING '/bin/cat' WHERE a.ds > '2008-08-09';

猜你喜欢

转载自blog.csdn.net/qq_41568597/article/details/82258358