不談情懷,只跟你談Hive SQL

簡介

Hive 是基於Hadoop 構建的一套數據倉庫分析系統,它提供了豐富的SQL查詢方式來分析存儲在Hadoop 分佈式文件系統中的數據,能夠將結構化的數據文件映射爲一張數據庫表,並提供完整的SQL查詢功能,能夠將SQL語句轉換爲MapReduce任務進行運行,經過本身的SQL 去查詢分析須要的內容,這套SQL 簡稱Hive SQL,使不熟悉mapreduce 的用戶很方便的利用SQL 語言查詢,彙總,分析數據。而mapreduce開發人員能夠把己寫的mapper和reducer 做爲插件來支持Hive 作更復雜的數據分析。node

它與關係型數據庫的SQL 略有不一樣,但支持了絕大多數的語句如DDL(data definition language)、DML(Data Manipulation Language) 以及常見的聚合函數、鏈接查詢、條件查詢。HIVE不適合用於聯機事務處理,也不提供實時查詢功能。它最適合應用在基於大量不可變數據的批處理做業。python

HIVE的特色:可伸縮(在Hadoop的集羣上動態的添加設備),可擴展,容錯,輸入格式的鬆散耦合sql

而後存儲什麼的不用說了,在hive簡介裏說的夠多了,並且只要你熟悉一種數據庫的話,大部分東西都是同樣的,好比Primary Type之類的,大同小異。咱們聊點兒不同的。數據庫

1. 複雜類型Complex Type

複雜類型能夠由初始類型(Primary Type)構建,也能夠經過其餘的複合類型構建,好比:express

  • Structs: the elements within the type can be accessed using the DOT (.) notation. For example, for a column c of type STRUCT {a INT; b INT}, the a field is accessed by the expression c.a

沒什麼好解釋的,點操做來訪問屬性值apache

  • Maps (key-value tuples): The elements are accessed using ['element name'] notation. For example in a map M comprising of a mapping from 'group' -> gid the gid value can be accessed using M['group']

映射取值的時候用M[],中括號。數組

  • Arrays (indexable lists): The elements in the array have to be in the same type. Elements can be accessed using the [n] notation where n is an index (zero-based) into the array. For example, for an array A having the elements ['a', 'b', 'c'], A[1] retruns 'b'.

下標也是從0開始的數組,可是數組中的元素必須爲同一類型。app

2. 內置運算與操做函數

這種東西每一個數據庫都有,我搞個表格放在這裏,供你們查閱。圖像顯示的過小,可將網頁放大再看~less

3. Hive SQL的功能

Hive SQL提供了相似於SQL的操做,它的操做對象爲表和分區,包括:分佈式

  • 能夠用WHERE語句從表中過濾出行
  • 能夠用SELECT語句從表中選出特定的列
  • 能夠鏈接兩個表
  • 可以計算在表中已經排序好的列集合
  • 可以將查詢結果儲存在另一個表中
  • 可以將一個表的內容下載到本地文件夾中
  • 可以將查詢結果存在HDFS路徑下
  • 可以操做表和分區:create,drop,alter
  • 可以在map/reduce流程中插入腳本

4. 用法用例之:DDL操做

•建表
•刪除表
•修改表結構
•建立/刪除視圖
•建立數據庫
•顯示命令

4.1 Creating Tables

CREATE TABLE page_view(viewTime INT, userid BIGINT,
                page_url STRING, referrer_url STRING,
                ip STRING COMMENT 'IP Address of the User')
COMMENT 'This is the page view table'
PARTITIONED BY(dt STRING, country STRING)
STORED AS SEQUENCEFILE;

這個例子中,表中的列被定義爲特定的相應類型。Comment註釋能夠看做是對列層面的,也能夠看做是整個表層面的。此外,PARTITIONED定義了分區列,分區列與表中的列是兩回事,它並非實際存在於表中。當用這種方式定義的時候,默認採用ASCII 001做爲域界定符,而同時用新行做爲行界定符。

固然,界定符還能夠用參數來表示:

CREATE TABLE page_view(viewTime INT, userid BIGINT,
                page_url STRING, referrer_url STRING,
                ip STRING COMMENT 'IP Address of the User')
COMMENT 'This is the page view table'
PARTITIONED BY(dt STRING, country STRING)
ROW FORMAT DELIMITED
        FIELDS TERMINATED BY '1'
STORED AS SEQUENCEFILE;

如上表所示,如今的行界定符不能夠被更改了,由於它採用的是Hadoop規定的定界符。

將表中的特定幾行進行散列也是個不錯的辦法,這樣會使得取樣查詢操做更加有效,可是若是不進行散列的話,上述操做也能夠進行,可是就沒有那麼的高效。下面的語句在userid的列上進行了分桶散列:

CREATE TABLE page_view(viewTime INT, userid BIGINT,
                page_url STRING, referrer_url STRING,
                ip STRING COMMENT 'IP Address of the User')
COMMENT 'This is the page view table'
PARTITIONED BY(dt STRING, country STRING)
CLUSTERED BY(userid) SORTED BY(viewTime) INTO 32 BUCKETS
ROW FORMAT DELIMITED
        FIELDS TERMINATED BY '1'
        COLLECTION ITEMS TERMINATED BY '2'
        MAP KEYS TERMINATED BY '3'
STORED AS SEQUENCEFILE;

在上面這個語句中,經過對userid使用了32個桶散列來達到了彙集表的目的,每個桶裏的數據按照viewTime的升序進行排列。這樣的組織結構能讓使用者更好地在彙集列上進行取樣操做。咱們再來看看下面的例子:

CREATE TABLE page_view(viewTime INT, userid BIGINT,
                page_url STRING, referrer_url STRING,
                friends ARRAY<BIGINT>, properties MAP<STRING, STRING>
                ip STRING COMMENT 'IP Address of the User')
COMMENT 'This is the page view table'
PARTITIONED BY(dt STRING, country STRING)
CLUSTERED BY(userid) SORTED BY(viewTime) INTO 32 BUCKETS
ROW FORMAT DELIMITED
        FIELDS TERMINATED BY '1'
        COLLECTION ITEMS TERMINATED BY '2'
        MAP KEYS TERMINATED BY '3'
STORED AS SEQUENCEFILE;

經過這個例子咱們再來總結一下Hive SQL中建表的幾個注意事項:

  • Comment能夠用於列level也能夠用於整個表level
  • 分區與data不一樣,它並不真正的與data存在一起,它實際上是一個目錄
  • cluster by規定了哪些列參與bucketing,而且規定了產生多少個bucket
  • Delimited規定了hive表中的分隔符
  • Stored as sequencefile指明瞭數據在hdfs中以二進制格式存儲的

最後表名和列名是大小寫敏感的

4.2 Browsing Tables and Partitions

SHOW TABLES;  列出數據倉庫中全部已存在的表
SHOW TABLES 'page.*';  列出全部以page開頭的表
SHOW PARTITIONS page_view;  列出該表的素有分區,若是該表沒有分區,那麼就會報錯
DESCRIBE page_view;  列出該表的列和列類型
DESCRIBE EXTENDED page_view;  列出該表的全部列和全部的屬性,這會輸出不少信息而且格式並不優美,經常用來調試

4.3 Altering Tables

ALTER TABLE old_table_name RENAME TO new_table_name;
對錶重命名,若是新名稱已經存在,則報錯
ALTER TABLE old_table_name REPLACE COLUMNS (col1 TYPE, ...);
對已存在的表中的某一列進行重命名,確保類型的正確性
ALTER TABLE tab1 ADD COLUMNS (c1 INT COMMENT 'a new int column', c2 STRING DEFAULT 'def val');
對錶新增一列

4.4 Dropping Tables and Partitions

DROP TABLE pv_users;
ALTER TABLE pv_users DROP PARTITION (ds='2008-08-08')

5. 加載數據

有不少種辦法可以將數據加載進Hive表中,用戶能夠建立一個external table來指向HDFS內的某一特定的位置,在這種用法中用戶能夠經過HDFS中的put和copy命令,將一份文件拷貝到一個特定的位置,同時也可以建立一個指向該位置的表,該表中的全部相關信息必須和拷貝的文件保持一致。一旦拷貝結束後,用戶能夠轉移這些數據而且能夠將這些數據插入到別的Hive表中。例如:若是文件/tmp/pv_2008-06-08.txt 包含以逗號做爲分隔符的page view(served on 2008-6-8),那麼這個page view就要採用合適的partition策略來加載到page_view表中:

CREATE EXTERNAL TABLE page_view_stg(viewTime INT, userid BIGINT,
                page_url STRING, referrer_url STRING,
                ip STRING COMMENT 'IP Address of the User',
                country STRING COMMENT 'country of origination')
COMMENT 'This is the staging page view table'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '44' LINES TERMINATED BY '12'
STORED AS TEXTFILE
LOCATION '/user/data/staging/page_view';
 
hadoop dfs -put /tmp/pv_2008-06-08.txt /user/data/staging/page_view
 
FROM page_view_stg pvs
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country='US')
SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip
WHERE pvs.country = 'US';

在上面這個例子中,nulls被插入到了目標表格中array和map類型的位置上,這個效果也能夠用外部表來實現,前提是表中的格式符合要求。此外,Hive系統還支持從本地文件系統的文件中直接加載數據,前提是輸入數據必須和表的格式相同。例如,若是文件/tmp/pv_2008-06-08_us.txt已經包含US數據(意思是us數據已通過濾好了),那麼咱們就不必再像上面例子中那樣作多餘的過濾操做,而直接採用下面的形式便可:

LOAD DATA LOCAL INPATH /tmp/pv_2008-06-08_us.txt INTO TABLE page_view 
PARTITION(date='2008-06-08', country='US')

在這個例子中輸入文件/tmp/pv_2008-06-08_us.txt很是大,用戶可能將數據進行分割進行並行計算(利用爲Hive定製的第三方工具)。當文件在HDFS中就緒的時候,下面這條語句能夠用來將數據加載到Hive標中。

LOAD DATA INPATH '/user/data/pv_2008-06-08_us.txt' INTO TABLE page_view 
PARTITION(date='2008-06-08', country='US')

It is assumed that the array and map fields in the input.txt files are null fields for these examples.

6. 查詢與插入數據

6.1 簡單的查詢

INSERT OVERWRITE TABLE user_active
SELECT user.*
FROM user
WHERE user.active = 1;

注意,與傳統SQL不一樣的是,咱們老是將結果插入到表中,稍後咱們將展現怎樣可以觀察這些數據結果甚至是將它們存進本地文件。你一樣能夠經過Beeline或者Hive CLI運行下面的查詢語句:

SELECT user.*
FROM user
WHERE user.active = 1;

此類操做將重寫部分臨時文件而且在客戶端顯示。

6.2 基於分區的查詢

分區在查詢中的使用自動取決於系統分區列的狀態。例如,咱們想獲得域名 xyz.com 2008年3月的全部的page_view,可使用以下語句:

INSERT OVERWRITE TABLE xyz_com_page_views
SELECT page_views.*
FROM page_views
WHERE page_views.date >= '2008-03-01' AND page_views.date <= '2008-03-31' AND
      page_views.referrer_url like '%xyz.com';

注意到page_views.date在這裏被使用到是由於上面的表中定義了: PARTITIONED BY(date DATETIME, country STRING) ;若是你將你的分區命名爲別的名字,那麼.date將不能正常的工做。

6.3 鏈接

爲了得到2008年3月3號的人口統計數據(根據性別)的page_view表,用戶須要鏈接page_view表和用戶數據表中的userid那一列。這個目標能夠經過以下語句達到:

INSERT OVERWRITE TABLE pv_users
SELECT pv.*, u.gender, u.age
FROM user u JOIN page_view pv ON (pv.userid = u.id)
WHERE pv.date = '2008-03-03';

爲了實現外鏈接,用能夠利用左鏈接,右鏈接或者全鏈接實現。例如,在上述狀況下爲了實現全外鏈接,對應的查詢語句能夠像下面那樣寫:

INSERT OVERWRITE TABLE pv_users
SELECT pv.*, u.gender, u.age
FROM user u FULL OUTER JOIN page_view pv ON (pv.userid = u.id)
WHERE pv.date = '2008-03-03';

若是要檢查另一個表中某個關鍵字的存在性,用戶可使用LEFT SEMI JOIN以下例所示

INSERT OVERWRITE TABLE pv_users
SELECT u.*
FROM user u LEFT SEMI JOIN page_view pv ON (pv.userid = u.id)
WHERE pv.date = '2008-03-03';

若是想鏈接多個表,可使用以下語法:

INSERT OVERWRITE TABLE pv_friends
SELECT pv.*, u.gender, u.age, f.friends
FROM page_view pv JOIN user u ON (pv.userid = u.id) JOIN friend_list f ON (u.id = f.uid)
WHERE pv.date = '2008-03-03';

6.4 聚合

爲了計算按全部用戶的數量,咱們能夠這樣寫查詢語句:

INSERT OVERWRITE TABLE pv_gender_sum
SELECT pv_users.gender, count (DISTINCT pv_users.userid)
FROM pv_users
GROUP BY pv_users.gender;

複合類型的聚合能夠同時實現,兩個聚合不能含有兩個不一樣的列,也就是說下面的語句是能夠實現的:

INSERT OVERWRITE TABLE pv_gender_agg
SELECT pv_users.gender, count(DISTINCT pv_users.userid), count(*), sum(DISTINCT pv_users.userid)
FROM pv_users
GROUP BY pv_users.gender;

然而這種操做是不容許的:

INSERT OVERWRITE TABLE pv_gender_agg
SELECT pv_users.gender, count(DISTINCT pv_users.userid), count(DISTINCT pv_users.ip)
FROM pv_users
GROUP BY pv_users.gender;

 

6.5 多表與文件插入

彙集或者簡單的選擇操做的結果在未來可能會送到多表中甚至會被送到HDFS文件中(能夠用hdfs工具操做)。若是根據性別來分類,用戶須要找到根據年齡來分類的page views,咱們能夠用下面的語句來實現:

FROM pv_users
INSERT OVERWRITE TABLE pv_gender_sum
    SELECT pv_users.gender, count_distinct(pv_users.userid)
    GROUP BY pv_users.gender
 
INSERT OVERWRITE DIRECTORY '/user/data/tmp/pv_age_sum'
    SELECT pv_users.age, count_distinct(pv_users.userid)
    GROUP BY pv_users.age;

第一條語句是將結果送到一個Hive表中,第二個結果是將結果送到HDFS文件中。

6.6 動態分區插入

在前面幾個例子中,用戶已經知道了哪一個分區將會嵌入,並且在一條插入語句中只有一個分區能夠被嵌入。若是你想加載進去多個分區的話,你必須使用multi-insert語句

FROM page_view_stg pvs
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country='US')
       SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip 
WHERE pvs.country = 'US'
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country='CA')
       SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip 
WHERE pvs.country = 'CA'
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country='UK')
       SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip 
WHERE pvs.country = 'UK';

爲了將某一天的全部國家分區數據所有加載,你必需要爲每個輸入數據建一個插入語句。這樣很是不方便由於你必需要提早知道輸入數據中的國家列表,而且提早建立分區。若是某一天這個列表發生了變更,你必須改變你的插入DML語句,隨之改變的,是建立的DDL語句。另一個致使不方便的緣由,是由於每一插入條語句要轉化成一個MapReduce做業。

動態分區插入正是爲了解決這個問題而設計的,它經過掃描全表輸入數據,進而決定哪些分區被建立或者增長。這個新特徵是在0.6.0的版本下新增的。在這種動態分區插入的方法中,輸入的列數據被用來估計判斷分區的哪一行應該被插入。若是某個分區沒有被建立,那麼就會自動建立該分區。利用這種特性,你只須要建立一條插入語句就能增長全部必要的分區。此外,既然只須要一個插入語句,就會只產生一個mapreduce做業,這將會顯著的改善Hadoop集羣的性能。

下面是一個用一條語句加載全部國家分區的例子:

FROM page_view_stg pvs
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country)
       SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url,
 null, null, pvs.ip, pvs.country

關於multi-insert語句有一些語法上的差別性:

  • 在PARTITION裏出現了country,可是沒有屬性值和它進行關聯。在這種狀況下,country是一個動態分區列。另外一方面,ds有一個關聯值,意味着它是一個靜態的分區列。若是一個列是動態分區列,那麼這個列的值將從輸入數據中得到。在目前的分區法則中,咱們只容許動態分區列成爲最後的列,由於分區列順序決定着它的繼承順序(dt是根分區,而country就是子分區)。你不能像這樣聲明一個分區:(dt, country='US'),由於這意味着你須要更新全部的以任何日期開頭而且以‘US’結尾的分區。
  • 一個額外的列:pvs.country被增長到選中的區域。這是與動態分區列相關聯的輸入列。注意到你不須要爲靜態分區列增長一個輸入列由於它的值在PARTITION中已是肯定了的。主要到動態分區列的值是根據排序來選擇的,而不是列名稱。同時做爲選中語句的最後一列。

動態分區查詢的語義:

  • 當已經存在關於動態分區列的非空分區,(舉個例子,country='CA'已經存在於根分區),原始的分區將會被覆蓋若是動態分區插入方法插入了相同的值。這符合insert overwrite這一語義。儘管如此,若是分區數據CA不存在於輸入數據,已存在的數據就不會被覆蓋。
  • 既然Hive的一個分區與HDFS的一個目錄關聯,那麼分區值必須與HDFS的路徑格式一致。任何一個字符在URL中都有特殊的意義。
  • 若是輸入列不是STRING類型的,那麼它首先會被轉化成STRING類型來構建HDFS路徑。
  • 若是輸入的列數據爲null或者一個空字符串,那麼對應的行會被放到一個特殊的分區中,這個分區的名字被Hive變量hive.exec.default.partition.name決定。默認的名稱是Hive_DEFAULT_PARTITION{}. 基本上來講這個分區包含了全部的「bad」行,也就是那些分區名稱不合法的值。訪問這些不合法的數據會獲得警告:分區值可能會丟失或者被Hive_DEFAULT_PARTITION取代,若是你選擇了Hive。JIRA HIVE-1309是一個用以解決「壞文件」的方式,它可以保持這些文件的分區值。
  • 動態分區插入可以潛在的成爲一個突起源,是由於它可以在短期內產生巨大的分區。爲了可以使你作好準備,咱們定義了三個變量:
  1. hive.exec.max.dynamic.partitions.pernode (default value being 100)系統可以經過每一個mapper或者reducer建立的最大動態分區數目。若是一個mapper或者一個reducer建立了超過閾值的分區,那麼一個致命的錯誤將會被拋出,同時這個job也會被killed。
  2. hive.exec.max.dynamic.partitions (default value being 1000)定義了一個DML語句可以建立的最大動態分區。若是mapper或者reducer沒有超過這個限制可是分區數已經超過了,那麼在job的末尾,也就是在中間數據被轉移到最終目的地以前會出現一個exception。
  3. hive.exec.max.created.files (default value being 100000)是mapper和reducer所能建立的最大文件數。無論什麼時候只要一個新文件被建立,Hadoop計數器就會按照這個原則來更新。若是總數已經超過了默認值,會拋出致命錯誤,job會被中止。
  • 咱們要防止動態分區插入的另外一個狀況是,用戶可能無心中指定全部的分區是動態分區,而不指定一個靜態分區,而最初的意圖是隻覆蓋一個根分區的子分區。咱們定義了另外一個參數hive.exec.dynamic.partition.mode=strict,以防止全部的動態分區狀況。在嚴格的模式下,您必須指定至少一個靜態分區。默認模式是嚴格的。此外,咱們有一個參數hive.exec.dynamic.partition=true/false來控制是否容許動態分區。在Hive 0.9.0以前默認值是false,而在Hive 0.9.0以後就是true。
  • 在Hive 0.6的版本中,將hive.merge.mapfiles設置爲true或者將hive.merge.mapredfiles設置爲true,動態分區插入失去做用,因此它內部關閉合並參數。在動態分區插入中的合併文件被支持在Hive 0.7中 (see JIRA HIVE-1307 for details).

最佳示例以下:

  • 如上所述,一個mapper或者reducer可能會建立出不少的分區,一個致命的錯誤將會被拋出而且做業將被中止:
  • beeline> set hive.exec.dynamic.partition.mode=nonstrict;
    beeline> FROM page_view_stg pvs
          INSERT OVERWRITE TABLE page_view PARTITION(dt, country)
                 SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip,
                        from_unixtimestamp(pvs.viewTime, 'yyyy-MM-dd') ds, pvs.country;
    ...
    2010-05-07 11:10:19,816 Stage-1 map = 0%,  reduce = 0%
    [Fatal Error] Operator FS_28 (id=41): fatal error. Killing the job.
    Ended Job = job_201005052204_28178 with errors
    ...

    一個mapper將會採用隨機行的集合,這將頗有可能致使不一樣的(dt, country)對會超過hive.exe.max.dynamic.partitions.pernode所規定的上限。一種曲線救國的方法是經過動態分區的列來組合這些行,同時將它們分佈在動態分區將會被建立的reducers裏面。在這個例子中不一樣的動態分區將會被顯著的減小,上述的查詢語句能夠寫成: 

  • beeline> set hive.exec.dynamic.partition.mode=nonstrict;
    beeline> FROM page_view_stg pvs
          INSERT OVERWRITE TABLE page_view PARTITION(dt, country)
                 SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip,
                        from_unixtimestamp(pvs.viewTime, 'yyyy-MM-dd') ds, pvs.country
                 DISTRIBUTE BY ds, country;

    附加閱讀:

  • Design Document for Dynamic Partitions
  • Hive DML: Dynamic Partition Inserts
  • HCatalog Dynamic Partitioning

6.7 插入到本地文件

在某些狀況下你想將輸出寫入到本地文件,因此你將數據導入到一張EXCEL表裏,這樣能夠用以下方式來實現。

INSERT OVERWRITE LOCAL DIRECTORY '/tmp/pv_gender_sum'
SELECT pv_gender_sum.*
FROM pv_gender_sum;

6.8 取樣

採樣子句容許用戶對數據的樣本進行寫查詢,而不是對整個表進行查詢。當前在按建立表語句的子句中的列中指定的列上進行了採樣。在下面的例子中咱們從32個buckets中選擇了第三個bucket的pv_gender_sum表:上面的示例查詢能夠重寫爲

INSERT OVERWRITE TABLE pv_gender_sum_sample
SELECT pv_gender_sum.*
FROM pv_gender_sum TABLESAMPLE(BUCKET 3 OUT OF 32);

通常表取樣語法能夠寫成這樣:

TABLESAMPLE(BUCKET x OUT OF y)

y必須是在表建立期間特殊指明的一個表數目的divsor或者multiple。桶的選擇決定於bucket_number模塊Y是否等於X,因此在上面的例子tablesample條款以下:

TABLESAMPLE(BUCKET 3 OUT OF 16)

這條語句將會挑選出第三和第19個buckets。buckets的編號將從0開始。

另外一方面這條語句將會挑選出第三個bucket:

TABLESAMPLE(BUCKET 3 OUT OF 64 ON userid)

我總感受抽樣這塊兒有點兒混亂

6.9 Union All

Hive語言同時也支持Union All,例如,咱們假設有兩個不一樣的表記錄了某個用戶發佈的一個video和用戶發表的評論,接下來的查詢鏈接了一個用戶的union all的一個結果,以便爲全部發布的video和評論建立一個帶有註釋的流。

INSERT OVERWRITE TABLE actions_users
SELECT u.id, actions.date
FROM (
    SELECT av.uid AS uid
    FROM action_video av
    WHERE av.date = '2008-06-03'
 
    UNION ALL
 
    SELECT ac.uid AS uid
    FROM action_comment ac
    WHERE ac.date = '2008-06-03'
    ) actions JOIN users u ON(u.id = actions.uid);

7.0 數組操做

表中的數組列能夠按照如下方式進行操做:

CREATE TABLE array_table (int_array_column ARRAY<INT>);

假設pv.friends的類型是ARRAY<INT>(也就是說是一個整形數組), 用戶能夠經過數組下標得到任何一個元素,以下所示:

SELECT pv.friends[2]
FROM page_views pv;

上面的SELECT表達式取得了pv.friends的第三列。

用戶一樣也可以經過使用size函數取得數組的長度:

SELECT pv.userid, size(pv.friends)
FROM page_view pv;

7.1 Map(聯合數組)操做

經過採用Hive語言原生支持的特色,用戶還能夠插入本身的自定義mapper和reducer的數據流。例如,爲了運行一個自定義mapper腳本map_script和自定義reducer腳本reduce_script減速器-用戶能夠發出如下命令以改變條款嵌入映射和減速器腳本。

請注意,列將被轉換爲字符串,並在給用戶腳本以前將其分隔爲字符串,而且用戶腳本的標準輸出將被視爲製表符分隔的字符串列。用戶腳本能夠輸出調試信息到標準錯誤,這將在Hadoop的任務詳細頁面顯示。

FROM (
     FROM pv_users
     MAP pv_users.userid, pv_users.date
     USING 'map_script'
     AS dt, uid
     CLUSTER BY dt) map_output
 
 INSERT OVERWRITE TABLE pv_users_reduced
     REDUCE map_output.dt, map_output.uid
     USING 'reduce_script'
     AS date, count;

map取樣的python文本

import sys
import datetime
 
for line in sys.stdin:
  line = line.strip()
  userid, unixtime = line.split('\t')
  weekday = datetime.datetime.fromtimestamp(float(unixtime)).isoweekday()
  print ','.join([userid, str(weekday)])

固然了,MAP和REDUCE對廣泛的選擇轉移的過程來講,都是一種語法糖。內查找能夠寫成這樣:

SELECT TRANSFORM(pv_users.userid, pv_users.date) USING 'map_script' AS dt, 
uid CLUSTER BY dt FROM pv_users;

Schema-less map/reduce: 若是在USING map_script後面沒有AS這個語句,Hive就會假設輸出文本包含兩個部分:key在第一個tab鍵前,value在這個tab鍵後面。注意到這跟指明瞭AS key, value的方式不同,由於在那種狀況下僅僅在第一個tab鍵和第二個tab鍵之間包含部分信息,若是有多個tab鍵的話。

使用這種方法,咱們容許用戶在不知道map輸出的schema的狀況下,移動舊的map/reduce文本。用戶仍然須要知道這個reduce輸出schema,由於它要跟表中的要插入的數據一一對應。

FROM (
    FROM pv_users
    MAP pv_users.userid, pv_users.date
    USING 'map_script'
    CLUSTER BY key) map_output
 
INSERT OVERWRITE TABLE pv_users_reduced
 
    REDUCE map_output.dt, map_output.uid
    USING 'reduce_script'
    AS date, count;

Distribute By and Sort By: 與其指明cluster by,用戶可使用distribute by或者sort by,這樣的話partition列和排序列就會不一樣,一般狀況下partition列是排序列的前綴,可是這並非必須的。

FROM (
    FROM pv_users
    MAP pv_users.userid, pv_users.date
    USING 'map_script'
    AS c1, c2, c3
    DISTRIBUTE BY c2
    SORT BY c2, c1) map_output
 
INSERT OVERWRITE TABLE pv_users_reduced
 
    REDUCE map_output.c1, map_output.c2, map_output.c3
    USING 'reduce_script'
    AS date, count;

7.2 Co-Groups

在使用map/reduce的用戶陣營中,cogroup是一個至關常見的操做,當用戶須要從多個表中發送數據到一個reducer,這樣的話行就會被按照表中特定列values的大小進行排列。經過使用UNION ALL操做和CLUSTER BY操做,這個目的能夠經過下面的Hive查詢語句實現。假設咱們但願cogroup表actions_video和表action_comments中的行,在uid列上。而後將它們發送到reduce_script用戶的reducer上,下面的語法能夠用來達到目的。

FROM (
     FROM (
             FROM action_video av
             SELECT av.uid AS uid, av.id AS id, av.date AS date
 
            UNION ALL
 
             FROM action_comment ac
             SELECT ac.uid AS uid, ac.id AS id, ac.date AS date
     ) union_actions
     SELECT union_actions.uid, union_actions.id, union_actions.date
     CLUSTER BY union_actions.uid) map
 
 INSERT OVERWRITE TABLE actions_reduced
     SELECT TRANSFORM(map.uid, map.id, map.date) USING 'reduce_script' AS (uid, id, reduced_val);
相關文章
相關標籤/搜索