Hive QL

转自http://www.alidata.org/archives/581

Hive 的官方文档中对查询语言有了很详细的描述,请参考:http://wiki.apache.org/hadoop/Hive/LanguageManual ,本文的内容大部分翻译自该页面,期间加入了一些在使用过程当中须要注意到的事项。node

Create Table

CREATE [EXTERNAL] TABLE [IF NOT EXISTS] table_name [(col_name data_type [COMMENT col_comment], ...)] [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] [ROW FORMAT row_format] [STORED AS file_format] [LOCATION hdfs_path] 

CREATE TABLE 建立一个指定名字的表。若是相同名字的表已经存在,则抛出异常;用户能够用 IF NOT EXIST 选项来忽略这个异常。正则表达式

EXTERNAL 关键字可让用户建立一个外部表,在建表的同时指定一个指向实际数据的路径(LOCATION),Hive 建立内部表时,会将数据移动到数据仓库指向的路径;若建立外部表,仅记录数据所在的路径,不对数据的位置作任何改变。在删除表的时候,内部表的元数据和数据会被一块儿删除,而外部表只删除元数据,不删除数据。express

LIKE 容许用户复制现有的表结构,可是不复制数据。apache

用户在建表的时候能够自定义 SerDe 或者使用自带的 SerDe。若是没有指定 ROW FORMAT 或者 ROW FORMAT DELIMITED,将会使用自带的 SerDe。在建表的时候,用户还须要为表指定列,用户在指定表的列的同时也会指定自定义的 SerDe,Hive 经过 SerDe 肯定表的具体的列的数据。缓存

若是文件数据是纯文本,可使用 STORED AS TEXTFILE。若是数据须要压缩,使用 STORED AS SEQUENCE 。数据结构

有分区的表能够在建立的时候使用 PARTITIONED BY 语句。一个表能够拥有一个或者多个分区,每个分区单独存在一个目录下。并且,表和分区均可以对某个列进行 CLUSTERED BY 操做,将若干个列放入一个桶(bucket)中。也能够利用SORT BY 对数据进行排序。这样能够为特定应用提升性能。oop

表名和列名不区分大小写,SerDe 和属性名区分大小写。表和列的注释是字符串。性能

Drop Table

删除一个内部表的同时会同时删除表的元数据和数据。删除一个外部表,只删除元数据而保留数据。spa

Alter Table

Alter table 语句容许用户改变现有表的结构。用户能够增长列/分区,改变serde,增长表和 serde 熟悉,表自己重命名。翻译

Add Partitions

ALTER TABLE table_name ADD  partition_spec [ LOCATION 'location1' ]  partition_spec [ LOCATION 'location2' ] ...  partition_spec: : PARTITION (partition_col = partition_col_value,  partition_col = partiton_col_value, ...) 

用户能够用 ALTER TABLE ADD PARTITION 来向一个表中增长分区。当分区名是字符串时加引号。

ALTER TABLE page_view ADD  PARTITION (dt='2008-08-08', country='us')  location '/path/to/us/part080808'  PARTITION (dt='2008-08-09', country='us')  location '/path/to/us/part080809'; 

DROP PARTITION

ALTER TABLE table_name DROP  partition_spec, partition_spec,... 

用户能够用 ALTER TABLE DROP PARTITION 来删除分区。分区的元数据和数据将被一并删除。

ALTER TABLE page_view  DROP PARTITION (dt='2008-08-08', country='us'); 

RENAME TABLE

ALTER TABLE table_name RENAME TO new_table_name 

这个命令可让用户为表改名。数据所在的位置和分区名并不改变。换而言之,老的表名并未“释放”,对老表的更改会改变新表的数据。

Change Column Name/Type/Position/Comment

ALTER TABLE table_name CHANGE [COLUMN]  col_old_name col_new_name column_type  [COMMENT col_comment]  [FIRST|AFTER column_name] 

这个命令能够容许用户修改一个列的名称、数据类型、注释或者位置。

好比:

CREATE TABLE test_change (a int, b int, c int);

ALTER TABLE test_change CHANGE a a1 INT; 将 a 列的名字改成 a1.

ALTER TABLE test_change CHANGE a a1 STRING AFTER b; 将 a 列的名字改成 a1,a 列的数据类型改成 string,并将它放置在列 b 以后。新的表结构为: b int, a1 string, c int.

ALTER TABLE test_change CHANGE b b1 INT FIRST; 会将 b 列的名字修改成 b1, 并将它放在第一列。新表的结构为: b1 int, a string, c int.

注意:对列的改变只会修改 Hive 的元数据,而不会改变实际数据。用户应该肯定保证元数据定义和实际数据结构的一致性。

Add/Replace Columns

ALTER TABLE table_name ADD|REPLACE  COLUMNS (col_name data_type [COMMENT col_comment], ...) 

ADD COLUMNS 容许用户在当前列的末尾增长新的列,可是在分区列以前。

REPLACE COLUMNS 删除之后的列,加入新的列。只有在使用 native 的 SerDE(DynamicSerDe or MetadataTypeColumnsetSerDe)的时候才能够这么作。

Alter Table Properties

ALTER TABLE table_name SET TBLPROPERTIES table_properties table_properties: : (property_name = property_value, property_name = property_value, ... ) 

用户能够用这个命令向表中增长 metadata,目前 last_modified_user,last_modified_time 属性都是由 Hive 自动管理的。用户能够向列表中增长本身的属性。可使用 DESCRIBE EXTENDED TABLE 来得到这些信息。

Add Serde Properties

ALTER TABLE table_name  SET SERDE serde_class_name  [WITH SERDEPROPERTIES serde_properties]  ALTER TABLE table_name  SET SERDEPROPERTIES serde_properties  serde_properties: : (property_name = property_value,  property_name = property_value, ... ) 

这个命令容许用户向 SerDe 对象增长用户定义的元数据。Hive 为了序列化和反序列化数据,将会初始化 SerDe 属性,并将属性传给表的 SerDe。如此,用户能够为自定义的 SerDe 存储属性。

Alter Table File Format and Organization

ALTER TABLE table_name SET FILEFORMAT file_format ALTER TABLE table_name CLUSTERED BY (col_name, col_name, ...)  [SORTED BY (col_name, ...)] INTO num_buckets BUCKETS 

这个命令修改了表的物理存储属性。

Loading files into table

当数据被加载至表中时,不会对数据进行任何转换。Load 操做只是将数据复制/移动至 Hive 表对应的位置。

Syntax:

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

Synopsis:

Load 操做只是单纯的复制/移动操做,将数据文件移动到 Hive 表对应的位置。

  • filepath 能够是:
    • 相对路径,例如:project/data1
    • 绝对路径,例如: /user/hive/project/data1
    • 包含模式的完整 URI,例如:hdfs://namenode:9000/user/hive/project/data1
  • 加载的目标能够是一个表或者分区。若是表包含分区,必须指定每个分区的分区名。
  • filepath 能够引用一个文件(这种状况下,Hive 会将文件移动到表所对应的目录中)或者是一个目录(在这种状况下,Hive 会将目录中的全部文件移动至表所对应的目录中)。
  • 若是指定了 LOCAL,那么:
    • load 命令会去查找本地文件系统中的 filepath。若是发现是相对路径,则路径会被解释为相对于当前用户的当前路径。用户也能够为本地文件指定一个完整的 URI,好比:file:///user/hive/project/data1.
    • load 命令会将 filepath 中的文件复制到目标文件系统中。目标文件系统由表的位置属性决定。被复制的数据文件移动到表的数据对应的位置。
  • 若是没有指定 LOCAL 关键字,若是 filepath 指向的是一个完整的 URI,hive 会直接使用这个 URI。 不然:
    • 若是没有指定 schema 或者 authority,Hive 会使用在 hadoop 配置文件中定义的 schema 和 authority,fs.default.name 指定了 Namenode 的 URI。
    • 若是路径不是绝对的,Hive 相对于 /user/ 进行解释。
    • Hive 会将 filepath 中指定的文件内容移动到 table (或者 partition)所指定的路径中。
  • 若是使用了 OVERWRITE 关键字,则目标表(或者分区)中的内容(若是有)会被删除,而后再将 filepath 指向的文件/目录中的内容添加到表/分区中。
  • 若是目标表(分区)已经有一个文件,而且文件名和 filepath 中的文件名冲突,那么现有的文件会被新文件所替代。

SELECT

Syntax

SELECT [ALL | DISTINCT] select_expr, select_expr, ... FROM table_reference [WHERE where_condition]  [GROUP BY col_list] [  CLUSTER BY col_list  | [DISTRIBUTE BY col_list]  [SORT BY col_list] ] [LIMIT number]  
  • 一个SELECT语句能够是一个union查询或一个子查询的一部分。
  • table_reference是查询的输入,能够是一个普通表、一个视图、一个join或一个子查询
  • 简单查询。例如,下面这一语句从t1表中查询全部列的信息。
SELECT * FROM t1 

WHERE Clause

where condition 是一个布尔表达式。例如,下面的查询语句只返回销售记录大于 10,且归属地属于美国的销售表明。Hive 不支持在WHERE 子句中的 IN,EXIST 或子查询。

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

ALL and DISTINCT Clauses

使用ALL和DISTINCT选项区分对重复记录的处理。默认是ALL,表示查询全部记录。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 hive> SELECT DISTINCT col1 FROM t1 1 2 

基于Partition的查询

通常 SELECT 查询会扫描整个表(除非是为了抽样查询)。可是若是一个表使用 PARTITIONED BY 子句建表,查询就能够利用分区剪枝(input pruning)的特性,只扫描一个表中它关心的那一部分。Hive 当前的实现是,只有分区断言出如今离 FROM 子句最近的那个WHERE 子句中,才会启用分区剪枝。例如,若是 page_views 表使用 date 列分区,如下语句只会读取分区为‘2008-03-01’的数据。

SELECT page_views.*  FROM page_views  WHERE page_views.date >= '2008-03-01'  AND page_views.date <= '2008-03-31'; 

HAVING Clause

Hive 如今不支持 HAVING 子句。能够将 HAVING 子句转化为一个字查询,例如:

SELECT col1 FROM t1 GROUP BY col1 HAVING SUM(col2) > 10 

能够用如下查询来表达:

SELECT col1 FROM (SELECT col1, SUM(col2) AS col2sum  FROM t1 GROUP BY col1) t2  WHERE t2.col2sum > 10 

LIMIT Clause

Limit 能够限制查询的记录数。查询的结果是随机选择的。下面的查询语句从 t1 表中随机查询5条记录:

SELECT * FROM t1 LIMIT 5 

Top k 查询。下面的查询语句查询销售记录最大的 5 个销售表明。

SET mapred.reduce.tasks = 1 SELECT * FROM sales SORT BY amount DESC LIMIT 5 

REGEX Column Specification

SELECT 语句可使用正则表达式作列选择,下面的语句查询除了 ds 和 hr 以外的全部列:

SELECT `(ds|hr)?+.+` FROM sales 

Join

Syntax

join_table: table_reference JOIN table_factor [join_condition] | table_reference {LEFT|RIGHT|FULL} [OUTER]  JOIN table_reference join_condition | table_reference LEFT SEMI JOIN  table_reference join_condition  table_reference: table_factor | join_table  table_factor: tbl_name [alias] | table_subquery alias | ( table_references )  join_condition: ON equality_expression ( AND equality_expression )*  equality_expression:  expression = expression 

Hive 只支持等值链接(equality joins)、外链接(outer joins)和(left semi joins???)。Hive 不支持全部非等值的链接,由于非等值链接很是难转化到 map/reduce 任务。另外,Hive 支持多于 2 个表的链接。

写 join 查询时,须要注意几个关键点:
1. 只支持等值join,例如:

SELECT a.* FROM a JOIN b ON (a.id = b.id)  SELECT a.* FROM a JOIN b  ON (a.id = b.id AND a.department = b.department)  

是正确的,然而:

SELECT a.* FROM a JOIN b ON (a.id b.id) 

是错误的。

2. 能够 join 多于 2 个表,例如

SELECT a.val, b.val, c.val FROM a JOIN b  ON (a.key = b.key1) JOIN c ON (c.key = b.key2) 

若是join中多个表的 join key 是同一个,则 join 会被转化为单个 map/reduce 任务,例如:

SELECT a.val, b.val, c.val FROM a JOIN b  ON (a.key = b.key1) JOIN c  ON (c.key = b.key1) 

被转化为单个 map/reduce 任务,由于 join 中只使用了 b.key1 做为 join key。

SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1)  JOIN c ON (c.key = b.key2) 

而这一 join 被转化为 2 个 map/reduce 任务。由于 b.key1 用于第一次 join 条件,而 b.key2 用于第二次 join。

join 时,每次 map/reduce 任务的逻辑是这样的:reducer 会缓存 join 序列中除了最后一个表的全部表的记录,再经过最后一个表将结果序列化到文件系统。这一实现有助于在 reduce 端减小内存的使用量。实践中,应该把最大的那个表写在最后(不然会由于缓存浪费大量内存)。例如:

SELECT a.val, b.val, c.val FROM a  JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1) 

全部表都使用同一个 join key(使用 1 次 map/reduce 任务计算)。Reduce 端会缓存 a 表和 b 表的记录,而后每次取得一个 c 表的记录就计算一次 join 结果,相似的还有:

SELECT a.val, b.val, c.val FROM a  JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2) 

这里用了 2 次 map/reduce 任务。第一次缓存 a 表,用 b 表序列化;第二次缓存第一次 map/reduce 任务的结果,而后用 c 表序列化。

LEFT,RIGHT 和 FULL OUTER 关键字用于处理 join 中空记录的状况,例如:

SELECT a.val, b.val FROM a LEFT OUTER  JOIN b ON (a.key=b.key) 

对应全部 a 表中的记录都有一条记录输出。输出的结果应该是 a.val, b.val,当 a.key=b.key 时,而当 b.key 中找不到等值的 a.key 记录时也会输出 a.val, NULL。“FROM a LEFT OUTER JOIN b”这句必定要写在同一行——意思是 a 表在 b 表的左边,因此 a 表中的全部记录都被保留了;“a RIGHT OUTER JOIN b”会保留全部 b 表的记录。OUTER JOIN 语义应该是遵循标准 SQL spec的。

Join 发生在 WHERE 子句以前。若是你想限制 join 的输出,应该在 WHERE 子句中写过滤条件——或是在 join 子句中写。这里面一个容易混淆的问题是表分区的状况:

SELECT a.val, b.val FROM a  LEFT OUTER JOIN b ON (a.key=b.key) WHERE a.ds='2009-07-07' AND b.ds='2009-07-07' 

会 join a 表到 b 表(OUTER JOIN),列出 a.val 和 b.val 的记录。WHERE 从句中可使用其余列做为过滤条件。可是,如前所述,若是 b 表中找不到对应 a 表的记录,b 表的全部列都会列出 NULL,包括 ds 列。也就是说,join 会过滤 b 表中不能找到匹配 a 表 join key 的全部记录。这样的话,LEFT OUTER 就使得查询结果与 WHERE 子句无关了。解决的办法是在 OUTER JOIN 时使用如下语法:

SELECT a.val, b.val FROM a LEFT OUTER JOIN b ON (a.key=b.key AND  b.ds='2009-07-07' AND  a.ds='2009-07-07') 

这一查询的结果是预先在 join 阶段过滤过的,因此不会存在上述问题。这一逻辑也能够应用于 RIGHT 和 FULL 类型的 join 中。

Join 是不能交换位置的。不管是 LEFT 仍是 RIGHT join,都是左链接的。

SELECT a.val1, a.val2, b.val, c.val FROM a JOIN b ON (a.key = b.key) LEFT OUTER JOIN c ON (a.key = c.key) 

先 join a 表到 b 表,丢弃掉全部 join key 中不匹配的记录,而后用这一中间结果和 c 表作 join。这一表述有一个不太明显的问题,就是当一个 key 在 a 表和 c 表都存在,可是 b 表中不存在的时候:整个记录在第一次 join,即 a JOIN b 的时候都被丢掉了(包括a.val1,a.val2和a.key),而后咱们再和 c 表 join 的时候,若是 c.key 与 a.key 或 b.key 相等,就会获得这样的结果:NULL, NULL, NULL, c.val。

LEFT SEMI JOIN 是 IN/EXISTS 子查询的一种更高效的实现。Hive 当前没有实现 IN/EXISTS 子查询,因此你能够用 LEFT SEMI JOIN 重写你的子查询语句。LEFT SEMI JOIN 的限制是, JOIN 子句中右边的表只能在 ON 子句中设置过滤条件,在 WHERE 子句、SELECT 子句或其余地方过滤都不行。

SELECT a.key, a.value FROM a  WHERE a.key in  (SELECT b.key FROM B); 

能够被重写为:

SELECT a.key, a.val FROM a LEFT SEMI JOIN b on (a.key = b.key)
相关文章
相关标签/搜索