Hive 编程之DDL、DML、UDF、Select总结

  Hive的基本理论与安装可参看作者上一篇博文《Apache Hive 基本理论与安装指南》。

一、Hive命令行

  所有的hive命令都可以通过hive命令行去执行,hive命令行中仍有许多选项。使用$hive -H查看:

  -e 选项后面可以直接接一个hql语句,不用进入到hive命令行用户接口再输入hql语句。

  -f 选项后面接一个hql语句的文件。

  -i 选项在hql语句执行之前的初始化hql文件。例如添加、导入等操作都可以写在这个hql语句文件中。

  在hive使用中,大多是写很多脚本,在hive命令行中输入是属于交互式的,这就必须有工作人员在工作,不适合做批量处理。可以使用-f或-i选项,一次性执行可以将所有的hql语句放在一个hql脚本文件中。例如:

$ mkdir -p /opt/shell/sql
$ vim /opt/shell/sql/test.sql
select * from table_name limit 5;
select * from table_name where professional="student";
select count(*) from table_name;
select count(*) from table_name group by professional;
select * from table_name where professional="student" order by age;

  将脚本保存退出后,再批量执行:

$ hive -f /opt/shell/sql/test.sql

二、Hive DDL(Data Definition Language)

  详见官方文档。重点在于创建表和创建函数。

1. 建表语法

  • 建表语句

  从官方文档的Create Table中可以查看hive建表的命令,如下:

CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name    -- (Note: TEMPORARY available in Hive 0.14.0 and later)
  [(col_name data_type [COMMENT col_comment], ... [constraint_specification])]
  [COMMENT table_comment]
  [PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]
  [CLUSTERED BY (col_name, col_name, ...) [SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS]
  [SKEWED BY (col_name, col_name, ...)                  -- (Note: Available in Hive 0.10.0 and later)]
     ON ((col_value, col_value, ...), (col_value, col_value, ...), ...)
     [STORED AS DIRECTORIES]
  [
   [ROW FORMAT row_format] 
   [STORED AS file_format]
     | STORED BY 'storage.handler.class.name' [WITH SERDEPROPERTIES (...)]  -- (Note: Available in Hive 0.6.0 and later)
  ]
  [LOCATION hdfs_path]
  [TBLPROPERTIES (property_name=property_value, ...)]   -- (Note: Available in Hive 0.6.0 and later)
  [AS select_statement];   -- (Note: Available in Hive 0.5.0 and later; not supported for external tables)
 
CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name
  LIKE existing_table_or_view_name
  [LOCATION hdfs_path];
  • 数据类型
data_type
  : primitive_type
  | array_type
  | map_type
  | struct_type
  | union_type  -- (Note: Available in Hive 0.7.0 and later)

  data_type,有两种类型:一种是复杂类型,例如array_type(ARRAY < data_type >)、map_type(MAP < primitive_type, data_type >)、struct_type(STRUCT < col_name : data_type [COMMENT col_comment], ...>)、union_type(UNIONTYPE < data_type, data_type, ... >);简单类型有SMALLINT(占2个字节的整数)、INT(占4个字节的整数)、BIGINT(占8个字节的整数)、BOOLEAN、FLOAT、DOUBLE、STRING、BINARY、TIMESTAMP、DECIMAL、DATE、VARCHAR(0.12之后)、CHAR(字符,0.13之后)。

primitive_type
  : TINYINT
  | SMALLINT
  | INT
  | BIGINT
  | BOOLEAN
  | FLOAT
  | DOUBLE
  | DOUBLE PRECISION -- (Note: Available in Hive 2.2.0 and later)
  | STRING
  | BINARY      -- (Note: Available in Hive 0.8.0 and later)
  | TIMESTAMP   -- (Note: Available in Hive 0.8.0 and later)
  | DECIMAL     -- (Note: Available in Hive 0.11.0 and later)
  | DECIMAL(precision, scale)  -- (Note: Available in Hive 0.13.0 and later)
  | DATE        -- (Note: Available in Hive 0.12.0 and later)
  | VARCHAR     -- (Note: Available in Hive 0.12.0 and later)
  | CHAR        -- (Note: Available in Hive 0.13.0 and later)
  • row_format
row_format
  : DELIMITED [FIELDS TERMINATED BY char [ESCAPED BY char]] [COLLECTION ITEMS TERMINATED BY char]
        [MAP KEYS TERMINATED BY char] [LINES TERMINATED BY char]
        [NULL DEFINED AS char]   -- (Note: Available in Hive 0.13 and later)
  | SERDE serde_name [WITH SERDEPROPERTIES (property_name=property_value, property_name=property_value, ...)]

  row_format:需要指出每一行是由什么隔开,并且要指出每一行中的每一个字段是用什么隔开的。

  对于字段的隔开,复杂数据类型和简单数据类型不一样。例如数组类型是复杂类型,其隔开符[COLLECTION ITEMS TERMINATED BY char],Map类型的要使用[MAP KEYS TERMINATED BY char]。[LINES TERMINATED BY char]可以设置行隔开符,如果不写,默认为回车换行。

  • file_format
file_format:
  : SEQUENCEFILE
  | TEXTFILE    -- (Default, depending on hive.default.fileformat configuration)
  | RCFILE      -- (Note: Available in Hive 0.6.0 and later)
  | ORC         -- (Note: Available in Hive 0.11.0 and later)
  | PARQUET     -- (Note: Available in Hive 0.13.0 and later)
  | AVRO        -- (Note: Available in Hive 0.14.0 and later)
  | INPUTFORMAT input_format_classname OUTPUTFORMAT output_format_classname

  SEQUENCEFILE表示序列压缩格式,以及RCFILE、ORC、PARQUET、AVRO都是压缩格式。TEXTFILE是默认的格式,文本格式。

2. 创建表案例一  

  • 查看数据

  为举例说明,这里先生成数据文件:

vim /root/person.txt
1,zhangsan,31,books-sports-math,city:shanghai-street:lujiazui-zipcode:313100
2,lisi,35,books-music,city:hangzhou-street:xihu-zipcode:222100
3,wangwu,25,computer-games,city:guangzhou-street:baiyun-zipcode:212300
  • 创建表

  根据数据创建一个人类表,包含人的编号、姓名、年龄、爱好、地址五个字段。

vim /opt/shell/sql/t_person.sql
create table t_person
(
    id int,
    name string,
    age int,
    likes array<string>,
    address map<string,string>
) row format delimited 
fields terminated by ',' 
collection items terminated by '-'
map keys terminated by ':' 
lines terminated by '\n' 
stored as textfile;

  根据表结构,字段之间按,隔开;likes的类型是array,所以可以有多个爱好,元素之间按-隔开,以,作为数组的结束;地址是map类型,也是collection类型, key和value按:隔开,每对key和value之间用-隔开(和collection一致);回车代表换行符。

  执行创建表脚本:

$ hive -f /opt/shell/sql/t_person.sql
  • 将数据从本地导入到hive
hive> load data local inpath '/root/person.txt' into table t_person;
  • 查看数据

 

3. 创建表案例二(分区表)

  当我们要在关系型数据库中查找某一数据时,一般需要匹配数据库中的所有数据,所以可以通过分区来提高查询效率。把常用的查询条件作为一种分区,例如要查某一天的数据,事实上就是可以只匹配当天的数据,其他数据不匹配。下面以建一个日志管理表为例:

  • 数据文件

  文件经常按照天、小时等进行分割。故在此创建多个文件如下:

vim /root/log_2016-03-11.txt
111,1,200,2016-03-11
135,4,100,2016-03-11
211,3,300,2016-03-11
141,1,200,2016-03-11
131,2,400,2016-03-11
vim /root/log_2016-03-12.txt
121,2,300,2016-03-12
125,3,400,2016-03-12
211,1,400,2016-03-12
161,3,100,2016-03-12
171,5,200,2016-03-12
  • 创建表结构
create table t_log (id string,type int,pid string,logtime string)
partitioned by (day string)
row format delimited 
fields terminated by ','
stored as textfile;

  这里指定了按照天做分区。

  • 装载数据
hive> load data local inpath '/root/log_2016-03-11.txt' into table t_log partition (day='2016-03-11');
hive> load data local inpath '/root/log_2016-03-12.txt' into table t_log partition (day='2016-03-12');
  • 查看数据
hive> select * from t_log;

  从上图可以看出,数据增加了一个字段,左边是logtime,右边是分区day。所以,添加一个分区可以理解为添加一个字段;但是又不同于添加一个字段,因为查询按照分区做限定(查询分区day='2016-03-11')时其他分区的数据不读取,而如果是字段的话,需要每一条数据进行匹配过滤。也可以从浏览器看出创建分区表的目录结构如下:

  创建分区,就会为每一个分区建一个目录,所以在查询时,只在限定的分区目录里查询。

  查看某一天的数据量

hive> select count(*) from t_log where day='2016-03-11';

  查看某一个月的数据量

hive>select count(*) from t_log where day between 'xxx' and 'xxx';

  当然,这里需要先转换格式,略。

三、Hive DML(Data Manipulation Language)

  详见官方文档。DML是对数据进行增删改的脚本。但是数据在hdfs中,实际上不提供修改,只提供追加(insert),支持删除。

1. Loading files into tables

  • 语法

  load file将数据导入到表中。其语法为:

hive> LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)]

  local:如果导入的数据在Linux主机本地,就要添加[local],如果数据已经在hdfs上就不能加local;overwrite:覆盖;partition:如果表有分区就要加partition。

  • 实例
    • 将hdfs中的数据装载到hive  

  首先,上传Linux文件到hdfs

$ hdfs dfs -put /root/log_2016-03-1* /usr/input

  其次,在hive中执行导入

hive>load data inpath '/usr/input/log_2016-03-12.txt' into table t_log partition (day='2016-03-12');

hive>load data inpath '/usr/input/log_2016-03-12.txt' overwrite into table t_log partition (day='2016-03-12');

  注意:导入的分区要存在,导入到hive表后原来hdfs路径下的文件就不复存在了,即存在hdfs的目录变了,实际就是元数据变了,block的位置没有变。如果使用overwrite,则原来该表中指定分区的数据将全部清空,然后上传该文件。如果load相同文件名名到表中时,会默认生成副本filename_copy_1。文件中的数据格式要和表的结构相吻合。

    • 将Linux本地的数据装载到hive
hive>load data local inpath '/root/log_2016-03-11.txt' into table t_log partition (day='2016-03-11');

2. Inserting data into hive tables from queries

  • 语法
hive> INSERT OVERWRITE TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...) [IF NOT EXISTS]] select_statement1 FROM from_statement;
hive> INSERT INTO TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...)] select_statement1 FROM from_statement;

  其中的select_statement1 FROM from_statement;是一个sql语句。这是用于查询hive中已经存在的数据,将查询结果给到一张新表中。

hive> INSERT INTO TABLE tablename [PARTITION (partcol1[=val1], partcol2[=val2] ...)] VALUES values_row [, values_row ...] 

  这个语句是和关系型数据库的insert语句用法是一样的。区别有三:一是,有partition;二是,可以一次插入多条数据;三是,这种语句是转成MapReduce去执行的。

  因为hive是一个工具,其作用是查询和统计hdfs上的数据的,对于查询结果一般就在控制台上显示,所以这个insert语句作用就是将查询的结果保存下来,用于后期的展示。

  • 实例
hive> insert into table t_log partition (day='2016-03-13’) values ('888',2,'100','2016-03-13’); 

  这个会启动MapReduce,因为数据在文件中不存在,要插到文件中相当于上传,会生成一个新的文件。这个不常用。所以实际上inset into语句的实际意义在于将查询结果放到新的表中。

3. Delete

  删除数据一般可以不在hive中删除,直接在hdfs上删除文件。因为删数据实际上就是删除文件。例如要删除之前插入到hive的t_log表day=2016-03-11分区下的log_2016-03-11.txt,可以执行如下命令:

#hdfs dfs -rm /user/hive/warehouse/t_log/day=2016-03-11/t_log-2016-03-11.txt

四、Hive UDF(Operators and User-Defined functions)

  详见官方文档

1. 内置运算符

  • 关系运算符

  见文档

  • 算术运算符

  见文档

  • 逻辑运算符

  见文档

  • 复杂类型构造函数(Complex Type Constructors)
    • 复杂类型函数或对象的构建

  【1】map(构造map对象)

  Creates a map with the given key/value pairs. (key1,value1,key2,value2,…)

  【2】struct(创建架构数组)

  Creates a struct with the given field values. (val1,val2,val3,…)。Struct的属性名和列名相对应。struct可以认为是面向对象的对象类型,是一个构造体,里面可以定义属性的值,field的名称是预先定义的,就像Java类的属性预先定义好了。

  【3】named_struct

  Creates a struct with the given field names and values. (name1,val1,name2,val2, …)。根据给定的Struct的属性名来创建构建数组

  【4】array(创建数组对象)

  Creates an array with the given elements. (val1,val2,val3,…)。把各(若干)个字段的值变成一个array。

  【5】create_union

  Creates a union type with value that is being pointes to by the tag parameter.

    • 复杂类型函数的运算符

  【1】A[n]

  A是array类型,n是int类型。A[n]表示数组A的第n+1个元素。

  【2】M[key]

  M是map类型,key是map集合中键的类型。M[key]是取map集合中键为key的值。注意使用是key需要使用单引号。

  【3】S.x

  S是struct类型,x表示struct中的某个属性。S.x,将会返回S这个struct中定义的x属性型中存储的值。

2. 内置函数

  • 数学函数

  • 集合操作函数

  • 类型转换函数

  • 日期函数

  • 条件/逻辑函数

   略。

  • 字符串函数

3. UDAF

  UDAF和UDF的区别在于传给函数的参数不一样。UDF表示传给函数的参数只有一个值或一行值;UDAF是给函数传多个值或者多行值的函数,例如count(*),sum(col)等都是多行的值。

  UDAF(User- Defined Aggregation Funcation) 聚集函数,多进一出,例如count/max/min等。

4. UDTF

  详见文档。

5. UDF

  HivePlugins,Creating Custom UDFs。自定义函数需要以下几个步骤:首先要自定义一个类,继承UDF;其次,打成jar包;第三,将jar包加载到hive的classpath;第四,创建自定义函数;第五,使用函数。

  例如,要创建一个将字符串转成日期的函数。

  • 自定义类
package com.huidoo.hive;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.hive.serde2.io.TimestampWritable;
import org.apache.hadoop.io.Text;

public class StringToDate extends UDF {
    /**
     * 定义evaluate方法。返回值类型定义为TimestampWritable
     * @param str
     * @param datefmt
     * @return
     * @throws Exception 
     */
    public TimestampWritable evaluate(final Text str,final Text datefmt) throws Exception{
        SimpleDateFormat sdf= new SimpleDateFormat(datefmt.toString()); //datefmt是传入的格式化字符串,需要创建SimpleDateFormat对象,Text类型要转成Java中的String类型。
        Date date= sdf.parse(str.toString()); //将字符串str,按照格式sdf转成java.util的Date类型
        TimestampWritable time=new TimestampWritable();//创建Hadoop的时间戳对象
        time.setTime(date.getTime());//将date转成Timestamp类型,然后使用Hadoop时间戳对象设置成相应的时间。
        return time;
    }
}
  • 打成jar包并上传到服务器

  略。std.jar

  • 将jar包加载到hive的classpath
hive> add jar /path/to/std.jar;
  •  创建函数

  语法:create 函数名(不能与内置的一样) as ‘自定义类的全限定名’;

hive> create function std as 'com.huidoo.hive.StringToDate';

  这就创建了一个函数,名为std。

  • 使用函数
hive> select std('2016-02-06 01:19:30','yyyy-MM-dd HH:mm:ss') from t_log limit 3;

五、Hive Select

1. 简单查询和使用/切换数据库

hive> select * from table_name;

  查看当前数据库:

hive> SELECT current_database();

  "db_name.table_name" allows a query to access tables in different databases.可以查询不同数据库中的表。

  使用某数据库:

hive> USE database_name;

2. where从句

hive>SELECT * FROM sales WHERE amount > 10 AND region = "US"

3. ALL and DISTINCT从句

  使用distinct关键字去重查询,如下列出两个查询的区别:

hive> SELECT col1, col2 FROM t1

    1 3

    1 3

    1 4

    2 5

hive> SELECT DISTINCT col1, col2 FROM t1

    1 3

    1 4

    2 5

4. Join从句

  join用于表连接。两个表即两个文件能连接起来就一定有相同点,这就是说其中一个文件中某域的值和另一个文件的某域的值相同,就可以连接。

hive> select col1,col2,col3 from t1 join t2 on+条件;

5. 基于Partition的查询

  这种查询语法就只要把partition当成一个字段即可。如果查询时要引入外键关联表也可以使用JION…ON语句。

  • 非关联查询

  语法:

hive> select table_name.* from table_name where table_name.partition_name +条件;

  案例:

SELECT page_views.*
FROM page_views
WHERE page_views.date >= '2016-03-01' AND page_views.date <= '2016-03-31'
  • 关联查询

  语法:

hive> select table_name1.* from table_name1 join table_name2 on (table_name1.外键 = table_name2.主键 And table_name1.partiton_name +条件);

  案例:

SELECT page_views.*
FROM page_views JOIN dim_users 
ON (page_views.user_id = dim_users.id AND page_views.date >= '2016-03-01' AND page_views.date <= '2016-03-31');

6. Group by从句

hive> select col[list] from table_name group by col;

7. Order/Sort by从句

  sort by和order by的用法基本一致,都是做排序的,一般用order by。例如:

hive> select * from t_log order by id;

8. Having从句

  having从句必须依附于group by从句,在group从句后面接having从句,相当于增加一个条件。having后面可以接聚合函数,例如sum、avg等。

  • 单纯的group by语句
hive> select day from t_log group by day;

  其查询结果为:

  2016-03-11

  2016-03-12

  • 添加having从句
hive>select day from t_log group by day having avg(id)>150;

  其查询结果为:

  2016-03-12

  • hiving同效于子查询
hive> select day from (select day, avg(id) as avgid from t_log group by day) table2 where table2.avgid>150;

  这里将t_log表通过select day, avg(id) as avgid from t_log group by day查询得到的结果作为表再查询的。和上述的hiving从句得到的结果一样。

9. Limit从句

  限定查询返回的行数。

hive> select * from t_log limit 5;
posted @ 2018-03-11 11:20  Huidoo_Yang  阅读(1657)  评论(0编辑  收藏  举报