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)
相關文章
相關標籤/搜索