Hive—學習筆記(一)


 主要內容:

  1、Hive的基本工能機制和概念

  2、hive的安裝和基本使用

  3、HQL

  4、hive的腳本化運行使用方式

  5、hive的基本語法--建表語法

  6、hive的基本語法--內部表和外部表.

  7、hive的基本語法--create建表 like as

  8、hive的基本語法--數據導入--從本地--從hdfs

  9、查詢語法

  10、數據類型

  11、hive函數


1.   什么是hive

hive本身是一個單機程序。轉在哪里都行,相對於hadoop來說就是一個hdfs的客戶端和yarn的客戶端,放在哪一台linux機器都無所謂,只要能鏈接上hadoop集群就可以,hive本身沒有負載,無非就是接收一個sql然后翻譯成mr,提交到yarn中去運行。

hive數據分析系統(數據倉庫,像一個倉庫一樣,存放着很多數據,而且可以做各種查詢、統計和分析,將結果放入新生的表中),的正常使用,需要

  1、mysql用來存放hdfs文件到二維表的描述映射信息,也就是元數據

  2、hadoop集群

    hdfs集群

    yarn集群

 

1.1. hive基本思想

Hive是基於Hadoop的一個數據倉庫工具(離線),可以將結構化的數據文件映射為一張數據庫表,並提供類SQL查詢功能。

 

1.2. 為什么使用Hive

  • 直接使用hadoop所面臨的問題

    人員學習成本太高

    項目周期要求太短

    MapReduce實現復雜查詢邏輯開發難度太大

  • 為什么要使用Hive

    操作接口采用類SQL語法,提供快速開發的能力。

    避免了去寫MapReduce,減少開發人員的學習成本。

    功能擴展很方便。

1.3. Hive的特點

  • 可擴展

   Hive可以自由的擴展集群的規模,一般情況下不需要重啟服務。

  • 延展性

  Hive支持用戶自定義函數,用戶可以根據自己的需求來實現自己的函數。

  • 容錯

  良好的容錯性,節點出現問題SQL仍可完成執行。

2.   hive的基本架構

 

Jobtracker是hadoop1.x中的組件,它的功能相當於:

  Resourcemanager+MRAppMaster

 

TaskTracker 相當於: 

  Nodemanager  +  yarnchild

 hive 2.0 以后的版本,底層的運算引擎已經不是mr了,而是spark

3.   hive安裝

3.1. 最簡安裝:用內嵌derby作為元數據庫

准備工作:安裝hive的機器上應該有HADOOP環境(安裝目錄,HADOOP_HOME環境變量)

安裝:直接解壓一個hive安裝包即可

此時,安裝的這個hive實例使用其內嵌的derby數據庫作為記錄元數據的數據庫

此模式不便於讓團隊成員之間共享協作

3.2. 標准安裝:將mysql作為元數據庫

mysql裝在哪里都可以,只要能提供服務,能被hive訪問就可以。

 下載mysql的rpm安裝包

 

.tar   .gz    .tar.gz的區別

.tar是講多個文件打包成一個文件,沒有壓縮,

.gz是壓縮

3.2.1.   mysql安裝

① 上傳mysql安裝包

② 解壓:

tar -xvf MySQL-5.6.26-1.linux_glibc2.5.x86_64.rpm-bundle.tar

 

 ③ 安裝mysql的server包

rpm -ivh MySQL-server-5.6.26-1.linux_glibc2.5.x86_64.rpm

依賴報錯:

缺perl(是一中編程語言,好比java編程缺少jdk一樣)

安裝perl

yum install perl

安裝libaio

安裝完perl以及libaio后 ,繼續重新安裝mysql-server

 


 

(可以配置一個本地yum源進行安裝:

1、先在vmware中給這台虛擬機連接一個光盤鏡像

2、掛載光驅到一個指定目錄:mount -t iso9660 -o loop /dev/cdrom /mnt/cdrom

3、修改yum源;將yum的配置文件中baseURL指向/mnt/cdrom

 

 1、鏈接光盤鏡像

 

 

2、掛載光驅

3、修改yum源


 

rpm -ivh MySQL-server-5.6.26-1.linux_glibc2.5.x86_64.rpm

 

又出錯:包沖突conflict with

移除老版本的沖突包:mysql-libs-5.1.73-3.el6_5.x86_64

rpm -e mysql-libs-5.1.73-3.el6_5.x86_64 --nodeps

 繼續重新安裝mysql-server

rpm -ivh MySQL-server-5.6.26-1.linux_glibc2.5.x86_64.rpm

 

成功后,注意提示:里面有初始密碼及如何改密碼的信息

 

初始密碼:

/root/.mysql_secret 

 

 

改密碼腳本:需要mysql客戶端

/usr/bin/mysql_secure_installation

  

④ 安裝mysql的客戶端包:

rpm -ivh MySQL-client-5.6.26-1.linux_glibc2.5.x86_64.rpm

⑤ 啟動mysql的服務端:

 service mysql start

Starting MySQL. SUCCESS!

 

 

⑥ 修改root的初始密碼:

/usr/bin/mysql_secure_installation

按提示,輸入生成的隨機密碼

 

 

 

⑦ 測試:

用mysql命令行客戶端登陸mysql服務器看能否成功

[root@mylove ~]# mysql -uroot -proot

mysql> show databases;

mysql>exit

 

⑧ 給root用戶授予從任何機器上登陸mysql服務器的權限:

mysql用戶權限控制比較嚴苛,光有用戶名,密碼還不行,mysql可以限制從哪個機器登錄過來的,默認只能從服務器所在的本機登錄過來。需要設置某一個用戶可以從哪台機器登錄過來。

授予root用戶,@任何機器,訪問任何庫的任何表的權限。

mysql> grant all privileges on *.* to 'root'@'%' identified by '你的密碼' with grant option;

Query OK, 0 rows affected (0.00 sec)

 mysql> flush privileges;

  Query OK, 0 rows affected (0.00 sec)

注意點:要讓mysql可以遠程登錄訪問

最直接測試方法:從windows上用Navicat去連接,能連,則可以,不能連,則要去mysql的機器上用命令行客戶端進行授權:

在mysql的機器上,啟動命令行客戶端:

mysql -uroot -proot

mysql>grant all privileges on *.* to 'root'@'%' identified by 'root的密碼' with grant option;
mysql>flush privileges;

 3.3、開始安裝hive

版本說明1.2.1: hive 2.0 以后的版本,底層的運算引擎已經不是mr了,而是spark

3.3.1、上傳並解壓縮安裝包

3.3.2、修改配置文件—元數據庫配置

目的:告知mysql在哪里;以及用戶名,密碼

原因:hive是java程序,需要使用jdbc去連接mysql數據庫

hive-site.xml

vi conf/hive-site.xml

 

<configuration>
<property>
<!-- 連接url --> <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true</value> <description>JDBC connect string for a JDBC metastore</description> </property> <property>
<!-- 連接的驅動類 --> <name>javax.jdo.option.ConnectionDriverName</name> <value>com.mysql.jdbc.Driver</value> <description>Driver class name for a JDBC metastore</description> </property> <property>
<!-- 用戶名 -->
<name>javax.jdo.option.ConnectionUserName</name> <value>root</value> <description>username to use against metastore database</description> </property> <property>
<!-- 密碼 --> <name>javax.jdo.option.ConnectionPassword</name> <value>root</value> <description>password to use against metastore database</description> </property> </configuration>

3.3.3、jdbc驅動包

上傳一個mysql的驅動jar包到hive的安裝目錄的lib中

3.3.4、配置環境變量

因為,hive要訪問hdfs,還要提交job到yarn。

1、配置HADOOP_HOMEHIVE_HOME到系統環境變量中:

/etc/profile

 

2、刷新配置文件

source /etc/profile

3.3.4、啟動hive

然后用命令啟動hive交互界面:  

[root@hdp20-04 ~]# hive-1.2.1/bin/hive

 

 

 4、hive使用方式

1、直接在交互界面輸入sql進行交互。

2、sql里可以直接使用java類型。

3、hive中建立數據后,會在hdfs中出現對象的庫名.db的文件夾

4、建表的目的是為了和數據文件進行映射;建立表之后(默認建立在default庫中),hive會在hdfs上建立對應的文件夾,文件夾的名字就是表名稱;

  4.1、只要hdfs文件夾中有了數據即文件,對應的表中就會有了記錄

  4.2、hdfs文件中的數據是按照分隔符進行切分的(value.toString().split("分割符"))(默認分隔符\001:ctrlA--不可見,cat命令下看不見);表的定義是保存在mysql中的。

 

以下是簡單的實例:hive中的數據庫,數據表——建庫,建表

 

創建數據文件,上傳到hdfs對應的目錄下;

上傳到hdfs

hive查詢

 


 hive有hdfs的對應

hdfs中的user文件夾下有hive,hive下有warehouse(倉庫),warehouse下有對應於表名的文件夾

 


 現在看一下mysql中的元數據信息(內部工作機制)

新建數據庫的描述信息

 新建數據表的描述信息

 

 表中的字段信息

總結:

hive建立一張表的內在機制:

  1、在mysql中記錄這張表的定義;

  2、在hdfs中創建目錄;

  3、只要把數據文件都到目錄下,就可以在hive中進行查詢了;

  4、因此,不同的hive只要是操作的同一個mysq,同一個hdfs集群,看到的數據是一致的;

4.1、最基本使用方式

啟動一個hive交互shell,寫sql拿響應,寫sql看結果,如上所示。

bin/hive

hive>

以下是一些便捷技巧設置

設置一些基本參數,讓hive使用起來更便捷,比如:

1、讓提示符顯示當前庫:

hive>set hive.cli.print.current.db=true;

2、顯示查詢結果時顯示表的字段名稱:

hive>set hive.cli.print.header=true;

 

3、但是這樣設置只對當前會話有效,重啟hive會話后就失效

解決辦法:

在linux的當前用戶主目錄中,編輯一個.hiverc(隱藏文件)文件,將參數寫入其中:

vi .hiverc(hive啟動的時候會自動去當前用戶目錄下加載這個文件)

set hive.cli.print.header=true;

set hive.cli.print.current.db=true;

 4.2、啟動hive服務使用

hive服務程序和hive本身不一樣的;hive本身就是一個單機版的交互式程序;而hive服務可以在后台運行,它監聽端口1000(默認);

此時有需要另個程序:

  1、hive服務程序

  2、hive服務的客戶端程序

hive服務以及hive服務客戶端

4.2.1、啟動hive服務

  1、前台啟動

  2、后台啟動

# 前台方式
bin/hiveserver2 
# 或者
bin/hiveserver2 -hiveconf hive.root.logger=DEBUG,console

 

上述啟動,會將這個服務啟動在前台,如果要啟動在后台,則命令如下:

表示在后台運行,但是標准輸出任然是在控制台

1 代表:標准輸出

> 代表:重定向

代表:錯誤輸出

jobs可以找到在后台運行的程序,然后 fg 1 將其切換到前台,ctrl ^ c 結束進程

若不想要任何的輸入,可以將標准輸入重定向到linux中的黑洞中。

&1 表示:引用

# nobup表示就算啟動該進程的linux用戶退出,該進程仍然運行
nohup
bin/hiveserver2 1>/dev/null 2>&1 &
# 除了root用戶,只要當前linux用戶退出,該進程就會被殺死
bin/hiveserver2 1>/dev/null 2>&1 &

 4.2.2、啟動hive服務客戶端beeline

  1、連接命令 !connect

  2、鏈接方式2,啟動beeline時給出參數鏈接

  

  3、輸入hdfs用戶密碼認證

  4、關閉連接,退出客戶端

啟動成功后,可以在別的節點上用beeline去連接

  方式(1)

[root@hdp20-04 hive-1.2.1]# bin/beeline 

 

 回車,進入beeline的命令界面

輸入命令連接hiveserver2

beeline> !connect jdbc:hive2//mini1:10000

 

(hadoop01是hiveserver2所啟動的那台主機名,端口默認是10000)

  方式(2)

啟動時直接連接:

bin/beeline -u jdbc:hive2://mini1:10000 -n root

接下來就可以做正常sql查詢了

hive服務客戶端連接hive服務用戶身份:因為要訪問hdfs,填入啟動hdfs的用戶身份,訪問hdfs不需要密碼

 使用實例(界面比上面的單機版hive交互界面好多了)

關閉connect,並沒有退出客戶端

退出hive服務的客戶端

 

 4.3、腳本化運行

大量的hive查詢任務,如果用交互式shell(hive本身,或者hive服務的客戶端都是交互式shell)來進行輸入的話,顯然效率及其低下,因此,生產中更多的是使用腳本化運行機制:

該機制的核心點是:hive可以用一次性命令的方式來執行給定的hql語句

[root@hdp20-04 ~]#  hive -e "insert into table t_dest select * from t_src;"

然后,進一步,可以將上述命令寫入shell腳本中,以便於腳本化運行hive任務,並控制、調度眾多hive任務,示例如下:

vi t_order_etl.sh

#!/bin/bash
hive -e "select * from db_order.t_order"
hive -e "select * from default.t_user"
hql="create table  default.t_bash as select * from db_order.t_order"
hive -e "$hql"

 

4.3.1、hive -e

hive -e:這是linux命令,而不是hive交互式shell,用來執行hive腳本;壓根不會進入hive的命令提示符界面,運行結束后回到linux的命令提示符中,這樣可以將很多hive操作,寫入一個腳本中。

 

例如:

把一張表中的數據查出來,插入到另外的一張數據表中

sql語法:

create table tt_A(......)
insert into tt_A select a,b,c form tt_B ....

新建腳本

執行腳本

查看hive中的結果

查看hdfs中的結果

 

 4.3.2、hive -f

如果要執行的hql語句特別復雜,那么,可以把hql語句寫入一個文件:

vi x.hql

select * from db_order.t_order;

select count(1) from db_order.t_user;

然后,用hive -f /root/x.hql 來執行 

例子:

vi test.hql

 

 4.3.3、區別

語法不一樣

  hive -e  “sql腳本”

  sql太復雜是可以將sql語句寫到腳本中,用hive -f來執行。

共同點:

  將hive查詢語句寫到shell腳本。


 

 

下面系統學習一下hive的語法(sql的語法極其相似)

5、hive建庫建表與數據導入

 

5.1、建庫

hive中有一個默認的庫:

  庫名: default

  庫目錄:hdfs://hdp20-01:9000/user/hive/warehouse

 

新建庫:

  create database db_order;

  庫名:庫建好后,在hdfs中會生成一個庫目錄(庫名.db):

  庫目錄:hdfs://hdp20-01:9000/user/hive/warehouse/db_order.db

5.2、建表

 

5.2.1.   基本建表語句

use db_order;

create table t_order(id string,create_time string,amount float,uid string);

 

表建好后,會在所屬的庫目錄中生成一個表目錄

/user/hive/warehouse/db_order.db/t_order

只是,這樣建表的話,hive會認為表數據文件中的字段分隔符^A

 

正確的建表語句為:

create table t_order(id string,create_time string,amount float,uid string)

row format delimited

fields terminated by ',';

 

 

這樣就指定了,我們的表數據文件中的字段分隔符為 ","

5.2.2.   刪除表

drop table t_order;

 

刪除表的效果是:

  hive會從元數據庫中清除關於這個表的信息;

  hive還會從hdfs中刪除這個表的表目錄; 

細節:

  內部表和外部表的刪除過程略有不同

  內部表:hdfs表目錄在warehouse/dbname.db/tableName,數據會放在該目錄下(默認:hive中建立的表是映射hdfs的warehouse下的數據)

  外部表:外部表可以任意指定表目錄的路徑,hive里面直接建立一張表去映射該目錄下的數據;hive各種操作生成的新表可以是內部表這樣就會放到hive默認的數據倉庫目錄里面,

      hdfs表目錄不在warehouse;數據不放在該目錄下,而是任意目錄,因為我們的數據采集程序不一定會把數據直接采集到hdfs下的warehouse中。此時我們也需要在hive找建立一張元數據表和這個hdfs目錄進行映射。

      外部表的優點:

        1、方便

        2、避免移走數據從而干擾采集程序的邏輯。 

    

5.2.3.   內部表與外部表

內部表(MANAGED_TABLE)表目錄按照hive的規范來部署,位於hive的倉庫目錄/user/hive/warehouse中

 

外部表(EXTERNAL_TABLE)表目錄由建表用戶自己指定

create external table t_access(ip string,url string,access_time string)

row format delimited

fields terminated by ','

location '/access/log';

 

外部表和內部表的特性差別:

  1、內部表的目錄在hive的倉庫目錄中 VS 外部表的目錄由用戶指定

  2、drop一個內部表時:hive會清除相關元數據,並刪除表數據目錄

  3、drop一個外部表時:hive只會清除相關元數據;

 外部表的作用:對接最原始的數據目錄,至於后面查詢生成的新表,用內部表就好。

一個hive的數據倉庫,最底層的表,一定是來自於外部系統,為了不影響外部系統的工作邏輯,在hive中可建external表來映射這些外部系統產生的數據目錄;

然后,后續的etl操作,產生的各種表建議用managed_table

 演示:

原始數據

原始數據上傳到hdfs非hive數據倉庫目錄

 

為了分析數據,需要在hive中建立一張外部表和數據目錄進行映射(映射關系:靠元數據來描述)

 

5.2.4.   分區表

分區表的實質是:在表目錄中為數據文件創建分區子目錄,以便於在查詢時,MR程序可以針對分區子目錄中的數據進行處理,縮減讀取數據的范圍

 

比如,網站每天產生的瀏覽記錄,瀏覽記錄應該建一個表來存放,但是,有時候,我們可能只需要對某一天的瀏覽記錄進行分析

這時,就可以將這個表建為分區表,每天的數據導入其中的一個分區;

當然,每日的分區目錄,應該有一個目錄名(分區字段)

 5.2.4.1、一個分區字段的實例

示例如下:

1、創建帶分區的表

create table t_access(ip string,url string,access_time string)
partitioned by(dt string)
row format delimited
fields terminated by ',';

注意:分區字段不能是表定義中的已存在字段,否組會沖突;以為分區字段也會被當成數組字段值被返回,其實這是一個偽字段;

2、查看分區

 

3、向分區中導入數據

hive提供了數據導入命令load,導入的時候需要指定分區,如果不指定直接導入主目錄下,本質同hadoop的hdfs上傳文件是一樣的。

load data local inpath '/root/access.log.2017-08-04.log' into table t_access partition(dt='20170804');

load data local inpath '/root/access.log.2017-08-05.log' into table t_access partition(dt='20170805');

4、針對分區數據進行查詢

分區表既可以查詢分區數據,也可以查詢全部數據。

a、統計8月4號的總PV:

select count(*) from t_access where dt='20170804';

實質:就是將分區字段當成表字段來用,就可以使用where子句指定分區了

b、統計表中所有數據總的PV:

select count(*) from t_access;

 

例子截圖:分區字段也會被當成數組字段值被返回,其實這是一個偽字段。

創建表:

數據文件1

數據文件2

  導入數據

 

查詢數據

hdfs(主分區與字分區)主目錄下的子目錄

 

5.2.4.2、多個分區字段示例

建表:

create table t_partition(id int,name string,age int)
partitioned by(department string,sex string,howold int)
row format delimited fields terminated by ',';

導數據:

load data local inpath '/root/p1.dat' into table t_partition partition(department='xiangsheng',sex='male',howold=20);

 

5.2.5.   CTAS建表語法

  1、like

  2、as

1、可以通過已存在表來建表:

create table t_user_2 like t_user;

 

新建的t_user_2表結構定義與源表t_user一致,但是沒有數據

 

 

2、在建表的同時插入數據

創建的表的字段與查詢語句的字段是一樣的

create table t_access_user 
as
select ip,url from t_access;

t_access_user會根據select查詢的字段來建表,同時將查詢的結果插入新表中

 

5.3. 數據導入導出

 

5.3.1   將數據文件導入hive的表

方式1:導入數據的一種方式:

手動用hdfs命令,將文件放入表目錄;

 

方式2:在hive的交互式shell中用hive命令來導入本地數據到表目錄

hive>load data local inpath '/root/order.data.2' into table t_order;

 

方式3:用hive命令導入hdfs中的數據文件到表目錄

hive>load data inpath '/access.log.2017-08-06.log' into table t_access partition(dt='20170806');

注意:導本地文件和導HDFS文件的區別:

  本地文件導入表:復制

  hdfs文件導入表:移動

 

5.3.2.   將hive表中的數據導出到指定路徑的文件

  insert overwrite

1、將hive表中的數據導入HDFS的文件

insert overwrite directory '/root/access-data'
row format delimited fields terminated by ','
select * from t_access;

 

2、將hive表中的數據導入本地磁盤文件

insert overwrite local directory '/root/access-data'
row format delimited fields terminated by ','
select * from t_access limit 100000;

 

5.3.3.   hive文件格式

HIVE支持很多種文件格式: SEQUENCE FILE | TEXT FILE | PARQUET FILE | RC FILE

create table t_pq(movie string,rate int)  stored as textfile;
create table t_pq(movie string,rate int)  stored as sequencefile;
create table t_pq(movie string,rate int)  stored as parquetfile;

 

5.4. 數據類型

 

5.4.1.   數字類型

TINYINT (1-byte signed integer, from -128 to 127)

SMALLINT (2-byte signed integer, from -32,768 to 32,767)

 

INT/INTEGER (4-byte signed integer, from -2,147,483,648 to 2,147,483,647)

 

BIGINT (8-byte signed integer, from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807)

FLOAT (4-byte single precision floating point number)

DOUBLE (8-byte double precision floating point number)

 

示例:

create table t_test(a string ,b int,c bigint,d float,e double,f tinyint,g smallint)

 

 

5.4.2.   日期時間類型

TIMESTAMP (Note: Only available starting with Hive 0.8.0) 【本質是長整型】

DATE (Note: Only available starting with Hive 0.12.0)

示例,假如有以下數據文件:

1,zhangsan,1985-06-30
2,lisi,1986-07-10
3,wangwu,1985-08-09

 

那么,就可以建一個表來對數據進行映射
create table t_customer(id int,name string,birthday date)
row format delimited fields terminated by ',';
--然后導入數據
load data local inpath '/root/customer.dat' into table t_customer;
--然后,就可以正確查詢

 

 

5.4.3.   字符串類型

STRING

VARCHAR (Note: Only available starting with Hive 0.12.0)

CHAR (Note: Only available starting with Hive 0.13.0)

 

 

5.4.4.   混雜類型

BOOLEAN

BINARY (Note: Only available starting with Hive 0.8.0)

1,zhangsan,true
2,lisi,false

create table t_p(id string,name string,marry boolean);

 

5.5.4.   復合類型(特殊)

  數據不是一個值,而是一個集合。

5.4.5.4、array數組類型

arrays: ARRAY<data_type> (Note: negative values and non-constant expressions are allowed as of Hive 0.14.)

示例:array類型的應用

假如有如下數據需要用hive的表去映射:

move.dat      

戰狼2,吳京:吳剛:龍母,2017-08-16
三生三世十里桃花,劉亦菲:癢癢,2017-08-20

設想:如果主演信息用一個數組來映射比較方便

建表:

create table t_movie(moive_name string,actors array<string>,first_show date)
row format delimited fields terminated by ',' collection items terminated by ':';

導入數據:

load data local inpath '/root/movie.dat' into table t_movie;

查詢:

select * from t_movie;
select moive_name,actors[0] from t_movie;

-- xx演員參演的電影
select moive_name,actors from t_movie where array_contains(actors,'吳剛');

-- 每部電影有幾個主演
select moive_name,size(actors) from t_movie;

 

 

5.4.5.2、map類型

maps: MAP<primitive_type, data_type> (Note: negative values and non-constant expressions are allowed as of Hive 0.14.)

 

1)         假如有以下數據:

1,zhangsan,father:xiaoming#mother:xiaohuang#brother:xiaoxu,28
2,lisi,father:mayun#mother:huangyi#brother:guanyu,22
3,wangwu,father:wangjianlin#mother:ruhua#sister:jingtian,29
4,mayun,father:mayongzhen#mother:angelababy,26

可以用一個map類型來對上述數據中的家庭成員進行描述

2)         建表語句:

create table t_person(id int,name string,family_members map<string,string>,age int)
row format delimited fields terminated by ',' collection items terminated by '#' map keys terminated by ':';

 

-- 導入數據
load data local inpath '/root/hivetest/fm.dat' into table t_family;

 

3)   查詢

+--------------+----------------+----------------------------------------------------------------+---------------+--+
| t_family.id  | t_family.name  |                    t_family.family_members                     | t_family.age  |
+--------------+----------------+----------------------------------------------------------------+---------------+--+
| 1            | zhangsan       | {"father":"xiaoming","mother":"xiaohuang","brother":"xiaoxu"}  | 28            |
| 2            | lisi           | {"father":"mayun","mother":"huangyi","brother":"guanyu"}       | 22            |
| 3            | wangwu         | {"father":"wangjianlin","mother":"ruhua","sister":"jingtian"}  | 29            |
| 4            | mayun          | {"father":"mayongzhen","mother":"angelababy"}                  | 26            |
+--------------+----------------+----------------------------------------------------------------+---------------+--+

 

-- 取map字段的指定key的值
select id,name,family_members['father'] as father from t_person;

 

-- 取map字段的所有key
-- 得到的是數組
select id,name,map_keys(family_members) as relation from t_person;

 

-- 取map字段的所有value
-- 得到的是數組 select id,name,map_values(family_members) from t_person;
select id,name,map_values(family_members)[0] from t_person;

  

-- 綜合:查詢有brother的用戶信息
select id,name,father 
from 
(select id,name,family_members['brother'] as father from t_person) tmp
where father is not null;

 

練習

-- 查出每個人的 爸爸、姐妹
select id,name,family_members["father"] as father,family_members["sister"] as sister,age
from t_family;

 

-- 查出每個人有哪些親屬關系
select id,name,map_keys(family_members) as relations,age
from  t_family;

 

-- 查出每個人的親人名字
select id,name,map_values(family_members) as members,age
from  t_family;

 

 

-- 查出每個人的親人數量
select id,name,size(family_members) as relations,age
from  t_family;

 

-- 查出所有擁有兄弟的人及他的兄弟是誰
-- 方案1:一句話寫完
select id,name,age,family_members['brother']
from t_family  where array_contains(map_keys(family_members),'brother');

 

-- 方案2:子查詢
select id,name,age,family_members['brother']
from
(select id,name,age,map_keys(family_members) as relations,family_members 
from t_family) tmp 
where array_contains(tmp.relations,'brother');

 

 

 

 

5.4.5.3、struct類型

帶結構的數據,帶有涵義,使用對象來描述,這里對象參照了C語言的機構體來表示類似對象的概念。

structs: STRUCT<col_name : data_type, ...>

1)假如有如下數據:

1,zhangsan,18:male:beijing 2,lisi,28:female:shanghai

其中的用戶信息包含:年齡:整數,性別:字符串,地址:字符串

設想用一個字段來描述整個用戶信息,可以采用struct

2)建表

drop table if exists t_user;

 

create table t_person_struct(id int,name string,info struct<age:int,sex:string,addr:string>)
row format delimited fields terminated by ',' collection items terminated by ':';

 

 

3)查詢

  對象.屬性

select * from t_person_struct;
select id,name,info.age from t_person_struct;

 

-- 查詢每個人的id name和地址
select id,name,info.addr
from t_user;

 

 

6、查詢語法

提示:在做小數據量查詢測試時,可以讓hive將mrjob提交給本地運行器運行,可以在hive會話中設置如下參數:

hive> set hive.exec.mode.local.auto=true;

 

6.1. 基本查詢示例

select * from t_access;

 

select count(*) from t_access;

 

select max(ip) from t_access;

 

sql中的單行函數聚合函數

select substr(name,0,2), age from t_user;

 

select name, sex, max(age) from t_user group by sex;

 

 

6.2. 條件查詢

select * from t_access where access_time<'2017-08-06 15:30:20'

 

select * from t_access where access_time<'2017-08-06 16:30:20' and ip>'192.168.33.3';

 

6.3. join關聯查詢示例

假如有a.txt文件

a,1
b,2
c,3
d,4

 

假如有b.txt文件

a,xx
b,yy
d,zz
e,pp

 

創建數據表,並導入數據

create table t_a(name string,numb int)
row format delimited
fields terminated by ',';

create table t_b(name string,nick string)
row format delimited
fields terminated by ',';

load data local inpath '/root/hivetest/a.txt' into table t_a;
load data local inpath '/root/hivetest/b.txt' into table t_b;

 

進行各種join查詢:

6.3.1、內連接

  不指定連接條件

  沒有指定連接條件—笛卡爾積

Join連接表的時候沒有指定兩張表怎么鏈接,就會出現全連接,就是笛卡爾積

-- 笛卡爾積
select a.*,b.*
from t_a a inner join t_b b;

 

  指定鏈接條件

  內連接:inner join(join)

-- 指定join條件
select a.*,b.*
from 
t_a a join t_b b on a.name=b.name;

 

 

6.3.2、左外鏈接:left outer join(left join)

  左外鏈接不加條件

  左外鏈接加條件—左表中的全部數據都會出現在結果集當中。

-- 左外連接(左連接)
select a.*,b.*
from 
t_a a left outer join t_b b on a.name=b.name;

 

6.3.3、右外鏈接:right outer join(right join)

  右鏈接不加條件

   右外鏈接加連接條件

-- 3/ 右外連接(右連接)
select a.*,b.*
from 
t_a a right outer join t_b b on a.name=b.name;

 

 

6.3.4、全外鏈接—full outer join(full join)

  沒有連接條件

  有鏈接條件—左右表的數據都有返回

-- 4/ 全外連接
select a.*,b.*
from
t_a a full outer join t_b b on a.name=b.name;

 

 

6.4. left semi join

hive中不支持exist/in子查詢,可以用left semi join來實現同樣的效果:

 返回滿足鏈接條件(有點像inner)的左表的數據,不返回右表的數據。

注意: left semi join的 select子句中,不能有右表的字段

-- 5/ 左半連接
select a.*
from 
t_a a left semi join t_b b on a.name=b.name;

因為:from后面是數據來源,而左半鏈接壓根不包含右表的數據。

 

6.5. group by分組聚合

select dt,count(*),max(ip) as cnt from t_access group by dt;

 

select dt,count(*),max(ip) as cnt from t_access group by dt having dt>'20170804';

 

select

dt,count(*),max(ip) as cnt

from t_access

where url='http://www.edu360.cn/job'

group by dt having dt>'20170804';

 

注意: 一旦有group by子句,那么,在select子句中就不能有 (分組字段,聚合函數) 以外的字段

 

## 為什么where必須寫在group by的前面,為什么group by后面的條件只能用having

因為,where是用於在真正執行查詢邏輯之前過濾數據用的

having是對group by聚合之后的結果進行再過濾;

 

上述語句的執行邏輯:

1、where過濾不滿足條件的數據

2、用聚合函數和group by進行數據運算聚合,得到聚合結果

3、用having條件過濾掉聚合結果中不滿足條件的數據


 

  (核心)

   與函數有關
  查看有哪些函數

show functions;

   可以用select+函數來測試函數

select upper("abc");

行內函數

表達式針對每一行運算一次

-- 針對每一行進行運算
select ip,upper(url),access_time  -- 該表達式是對數據中的每一行進行逐行運算
from t_pv_log;

 聚合函數

表達式針對每一組運算一次:一組得到唯一的一行結果,這里查詢select ip,是不允許的,因為其結果不唯一。

下圖是按照url分組后的數據;分組的目的是為了聚合;聚合結果必須唯一

也就是:select 要求查詢的字段,只能在一組數據中產生一個結果。一組產生一個結果。

或者說:select中出現的字段要么是分組字段,要么是聚合函數(對多條數據產生一個值)。

 

-- 求每條URL的訪問總次數

select url,count(1) as cnts   -- 該表達式是對分好組的數據進行逐組運算
from t_pv_log 
group by url;

 

   max()也是聚合函數,對分組后的數據進行聚合

-- 求每個URL的訪問者中ip地址最大的

select url,max(ip)
from t_pv_log
group by url;

 

 

-- 求每個用戶訪問同一個頁面的所有記錄中,時間最晚的一條

select ip,url,max(access_time) 
from  t_pv_log
group by ip,url;

此時數據分成了多組; 

 

 分組聚合綜合示例

-- 分組聚合綜合示例
-- 有如下數據
/*
192.168.33.3,http://www.edu360.cn/stu,2017-08-04 15:30:20
192.168.33.3,http://www.edu360.cn/teach,2017-08-04 15:35:20
192.168.33.4,http://www.edu360.cn/stu,2017-08-04 15:30:20
192.168.33.4,http://www.edu360.cn/job,2017-08-04 16:30:20
192.168.33.5,http://www.edu360.cn/job,2017-08-04 15:40:20


192.168.33.3,http://www.edu360.cn/stu,2017-08-05 15:30:20
192.168.44.3,http://www.edu360.cn/teach,2017-08-05 15:35:20
192.168.33.44,http://www.edu360.cn/stu,2017-08-05 15:30:20
192.168.33.46,http://www.edu360.cn/job,2017-08-05 16:30:20
192.168.33.55,http://www.edu360.cn/job,2017-08-05 15:40:20


192.168.133.3,http://www.edu360.cn/register,2017-08-06 15:30:20
192.168.111.3,http://www.edu360.cn/register,2017-08-06 15:35:20
192.168.34.44,http://www.edu360.cn/pay,2017-08-06 15:30:20
192.168.33.46,http://www.edu360.cn/excersize,2017-08-06 16:30:20
192.168.33.55,http://www.edu360.cn/job,2017-08-06 15:40:20
192.168.33.46,http://www.edu360.cn/excersize,2017-08-06 16:30:20
192.168.33.25,http://www.edu360.cn/job,2017-08-06 15:40:20
192.168.33.36,http://www.edu360.cn/excersize,2017-08-06 16:30:20
192.168.33.55,http://www.edu360.cn/job,2017-08-06 15:40:20

*/
-- 建表映射上述數據
create table t_access(ip string,url string,access_time string)
partitioned by (dt string)
row format delimited fields terminated by ',';


-- 導入數據
load data local inpath '/root/hivetest/access.log.0804' into table t_access partition(dt='2017-08-04');
load data local inpath '/root/hivetest/access.log.0805' into table t_access partition(dt='2017-08-05');
load data local inpath '/root/hivetest/access.log.0806' into table t_access partition(dt='2017-08-06');

-- 查看表的分區
show partitions t_access;
View Code

 

 過濾數據用where,過濾分組用having

where直接對from后的數據源進行過濾,group by是對過濾后的數據進行分組,having是過濾分組后的組。

錯誤示例

正確示例

添加分組字段,或者添加聚合,或者添加常量表達式(一組就一個值)

select dt,max(url),count(1),max(ip)
from t_access
where url='http://www.edu360.cn/job'
group by dt having dt>'2017-08-04';

 

select dt,url,count(1),max(ip)
from t_access
where url='http://www.edu360.cn/job'
group by dt,url having dt>'2017-08-04';

 

-- 求8月4號以后,每天http://www.edu360.cn/job的總訪問次數,及訪問者中ip地址中最大的
select dt,'http://www.edu360.cn/job',count(1),max(ip)
from t_access
where url='http://www.edu360.cn/job'
group by dt having dt>'2017-08-04';

-- 或者

-- 求8月4號以后,每天http://www.edu360.cn/job的總訪問次數,及訪問者中ip地址中最大的
select dt,'http://www.edu360.cn/job',count(1),max(ip)
from t_access
where url='http://www.edu360.cn/job' and dt>'2017-08-04'
group by dt;

 

 

6.6. 子查詢

把某一次查詢當作一張新的表,在這張表的基礎上進行(from,where等操作)再一次的查詢。

 

-- 求8月4號以后,每天每個頁面的總訪問次數,及訪問者中ip地址中最大的

select dt,url,count(1),max(ip)
from t_access
where dt>'2017-08-04'
group by dt,url;

 

-- 求上述結果中(求8月4號以后,每天每個頁面的總訪問次數,及訪問者中ip地址中最大的),且,只查詢出總訪問次數>2 的記錄

--
方式1: select dt,url,count(1) as cnts,max(ip) from t_access where dt>'2017-08-04' group by dt,url having cnts>2; -- 方式2:用子查詢 select dt,url,cnts,max_ip from (select dt,url,count(1) as cnts,max(ip) as max_ip from t_access where dt>'2017-08-04' group by dt,url) tmp where cnts>2;

 

 

7.   hive函數使用

-- 查看全部函數
show functions;

 

小技巧:測試函數的用法,可以專門准備一個專門的dual表

create table dual(x string);

insert into table dual values('');

 

其實:直接用常量來測試函數即可

select substr("abcdefg",1,3);

 

 

hive的所有函數手冊:

https://cwiki.apache.org/confluence/display/Hive/LanguageManual+UDF#LanguageManualUDF-Built-inTable-GeneratingFunctions(UDTF)

數據庫sql中的字符串角標是從1開始的。

7.1. 常用內置函數

7.1.1.   類型轉換函數

  cast(param1 as type)

cast與java中的類型強轉類似(java中一般來講這兩種數據具有繼承關系);與scala中的隱式轉換類似(不要求繼承關系,需要程序員提供轉換邏輯,告知如何轉換)

select cast("5" as int) from dual;
select cast("2017-08-03" as date) ;
select cast(current_timestamp as date);

 current_timestamp 是hive里的時間戳(java里的時間戳是一個長整型),其實就是一個常量

 

長整型時間戳

 

示例

1    1995-05-05 13:30:59    1200.3
2    1994-04-05 13:30:59    2200
3    1996-06-01 12:20:30    80000.5

 

create table t_fun(id string,birthday string,salary string)
row format delimited fields terminated by ',';

select id,cast(birthday as date) as bir,cast(salary as float) from t_fun;

 

 

 例子:

 

7.1.2.   數學運算函數

select round(5.4);   -- 5
select round(5.1345,3);  --5.135
select ceil(5.4); // select ceiling(5.4);   -- 6
select floor(5.4);  -- 5
select abs(-5.4);  -- 5.4
select greatest(3,5);  -- 5 
select greatest(3,5,6);  -- 6 
select least(3,5,6);

 

 

示例:

有表如下:

 

select greatest(cast(s1 as double),cast(s2 as double),cast(s3 as double)) from t_fun2;

  結果:

+---------+--+
|   _c0   |
+---------+--+
| 2000.0  |
| 9800.0  |
+---------+--+

 

 

select max(age) from t_person;  --  聚合函數 凡是可以比大小的字段都可以用max
select min(age) from t_person;   -- 聚合函數

 

 

7.1.3.   字符串函數

substr

substr(string, int start)   -- 截取子串
-- 類似java中的 substring(string, int start)
-- 示例:
-- 下標從1開始
select substr("abcdefg",2); substr(string, int start, int len) --類似java中的 substring(string, int start, int len) --示例:
select substr("abcdefg",2,3);

 mysql中特殊的函數(hive中沒有): 按照給定的分隔符切割后,返回指定個數的結果

 concat

 concat_ws

concat(string A, string B...)  -- 拼接字符串
-- 中間帶分隔符
concat_ws(string SEP, string A, string B...)
--示例:
select concat("ab","xy") from dual;
select concat_ws(".","192","168","33","44") from dual;

 

 

 

  length

length(string A)
--示例:
select length("192.168.33.44") from dual;

 

  split

split(string str, string pat)
--示例:select split("192.168.33.44",".") ; 錯誤的,因為.號是正則語法中的特定字符 select split("192.168.33.44","\\.");

 

  upper

upper(string str) --轉大寫

 

7.1.4.   時間函數

 時間常量

select current_timestamp; -- 常量

select current_date; -- 常量

 

-- 取當前時間的毫秒數時間戳 
select unix_timestamp();

 

 轉換

unix時間戳轉字符串

 

-- unix時間戳轉字符串
from_unixtime(bigint unixtime[, string format])
--示例:
select from_unixtime(unix_timestamp());

select from_unixtime(unix_timestamp(),"yyyy/MM/dd HH:mm:ss"); 


   字符串轉unix時間戳

-- 字符串轉unix時間戳
unix_timestamp(string date, string pattern)
--示例:
select unix_timestamp("2017-08-10 17:50:30");

select unix_timestamp("2017/08/10 17:50:30","yyyy/MM/dd HH:mm:ss");

 

 

   將字符串轉成日期date(不用給定格式,用標准格式)

-- 將字符串轉成日期date
select to_date("2017-09-17 16:58:32");

 

7.1.5.   表生成函數

    函數可以生成一張表。

    正常select可以生成一張表。

    目的:將結構化的數據打散

 

7.1.5.1、行轉列函數:explode()

假如有以下數據:

1,zhangsan,化學:物理:數學:語文
2,lisi,化學:數學:生物:生理:衛生
3,wangwu,化學:語文:英語:體育:生物

 

映射成一張表

create table t_stu_subject(id int,name string,subjects array<string>)
row format delimited fields terminated by ','
collection items terminated by ':';

使用explode()對數組字段“炸裂”

然后,我們利用這個explode的結果,來求去重的課程:

select distinct tmp.sub
from 
(select explode(subjects) as sub from t_stu_subject) tmp;

 

7.1.5.2、表生成函數:lateral view

  橫向連接

select id,name,tmp.sub 
from t_stu_subject lateral view explode(subjects) tmp as sub;

 

理解: lateral view 相當於兩個表在join

左表:是原表

右表:是explode(某個集合字段)之后產生的表

而且:這個join只在同一行的數據間進行

 

那樣,可以方便做更多的查詢:

比如,查詢選修了生物課的同學

 

select a.id,a.name,a.sub from 
(select id,name,tmp.sub as sub from t_stu_subject lateral view explode(subjects) tmp as sub) a
where sub='生物';

 

 

7.1.6.   集合函數

array_contains(Array<T>, value)  返回boolean值

-- 示例:
select moive_name,array_contains(actors,'吳剛') from t_movie;
select array_contains(array('a','b','c'),'c');

 

 

sort_array(Array<T>) 返回排序后的數組

--示例:
-- 直接使用構造函數進行模擬
select sort_array(array('c','b','a'));
-- 前面查詢的是常量,與后面查詢的表無關
select 'haha',sort_array(array('c','b','a')) as xx from (select 0) tmp;

 

直接使用構造函數進行模擬

 

size(Array<T>)  返回一個int值

--示例:
select moive_name,size(actors) as actor_number from t_movie;

 

size(Map<K.V>)  返回一個int值

map_keys(Map<K.V>)  返回一個數組

map_values(Map<K.V>) 返回一個數組

 

7.1.7.   條件控制函數

7.1.7.1、case when

語法:

  

語法:

CASE   [ expression ]

       WHEN condition1 THEN result1

       WHEN condition2 THEN result2

       ...

       WHEN conditionn THEN resultn

       ELSE result

END

示例:
select id,name,
case
when age<28 then 'youngth'
when age>27 and age<40 then 'zhongnian'
else 'old'
end
from t_user; 

 

0: jdbc:hive2://localhost:10000> select * from t_user;
+------------+--------------+----------------------------------------+--+
| t_user.id  | t_user.name  |              t_user.info               |
+------------+--------------+----------------------------------------+--+
| 1          | zhangsan     | {"age":18,"sex":"male","addr":"深圳"}    |
| 2          | lisi         | {"age":28,"sex":"female","addr":"北京"}  |
| 3          | wangwu       | {"age":38,"sex":"male","addr":"廣州"}    |
| 4          | 趙六           | {"age":26,"sex":"female","addr":"上海"}  |
| 5          | 錢琪           | {"age":35,"sex":"male","addr":"杭州"}    |
| 6          | 王二           | {"age":48,"sex":"female","addr":"南京"}  |
+------------+--------------+----------------------------------------+--+
需求:查詢出用戶的id、name、年齡(如果年齡在30歲以下,顯示年輕人,30-40之間,顯示中年人,40以上老年人)
select id,name,
case
when info.age<30 then '青年'
when info.age>=30 and info.age<40 then '中年'
else '老年'
end
from t_user;

 

7.1.7.2、 IF

  類似java里的三元表達式

select id,if(age>25,'working','worked') from t_user;

select moive_name,if(array_contains(actors,'吳剛'),'好電影','rom t_movie;

 

7.1.8.   json解析函數:表生成函數

  hive中有很多自帶的json解析函數。

json_tuple函數

示例:

select json_tuple(json,'movie','rate','timeStamp','uid') as(movie,rate,ts,uid) from t_rating_json;

產生結果:

利用json_tuple從原始json數據表中,etl 出一個詳細信息表:

create table t_rate2
as
select json_tuple(json,'movie','rate','timeStamp','uid') as (movie, rate, ts, uid)
from t_tatingjson;

 

 json_tuple不會對嵌套的json進行解析,會將嵌套的json直接當作某一個字段的value返回,可以通過用自定以函數的方式對嵌套的json進行解析。

create table t_rate 
as 
select 
uid,
movie,
rate,
year(from_unixtime(cast(ts as bigint))) as year,
month(from_unixtime(cast(ts as bigint))) as month,
day(from_unixtime(cast(ts as bigint))) as day,
hour(from_unixtime(cast(ts as bigint))) as hour,
minute(from_unixtime(cast(ts as bigint))) as minute,
from_unixtime(cast(ts as bigint)) as ts
from 
(select 
json_tuple(rateinfo,'movie','rate','timeStamp','uid') as(movie,rate,ts,uid)
from t_json) tmp
;

 

 

 

7.1.9.   分析函數:row_number() over()——分組TOPN

 業務場景:業務報表中常見

7.1.9.1、需求

有如下數據:

1,18,a,male
2,19,b,male
3,22,c,female
4,16,d,female
5,30,e,male
6,26,f,female

 需要查詢出每種性別中年齡最大的2條數據

 分析過程

  分組,排序,對每一組添加標記序號總段

-- 分組
-- 排序
5,30,e,male
2,19,b,male
1,18,a,male

6,26,f,female
3,22,c,female
4,16,d,female

--對每一組添加標記序號字段
5,30,e,male,1
2,19,b,male,2
1,18,a,male,3

6,26,f,female,1
3,22,c,female,2
4,16,d,female,3

 

7.1.9.2、實現

使用row_number函數,對表中的數據按照性別分組,按照年齡倒序排序並進行標記

hql代碼:

select id,age,name,sex,
row_number() over(partition by sex order by age desc) as rank from t_rownumber

產生結果

然后,利用上面的結果,使用子查詢,查詢出rank<=2的即為最終需求

select id,age,name,sex
from 
(
select id,age,name,sex, row_number() over(partition by sex order by age desc) as rank from t_rownumber) tmp
where rank<=2;

 

練習:求出電影評分數據中,每個用戶評分最高的topn條數據

7.1.10、窗口分析函數 — sum() over()

  作用:可以實現在窗口中進行逐行累加。

  over() — 就是一個窗口:即 若干條數據

      — 窗口如何表示 over (partition by uid order by month)

  sum() — 表示在這個窗口中求和

      — 窗口怎么求和,哪些行求和 sum (amount) over(partition by uid order by month rows between unbounded preceding and current row)

      — 回溯前面所有行,到當前行

-- 窗口分析函數  sum() over()  :可以實現在窗口中進行逐行累加

0: jdbc:hive2://localhost:10000> select * from  t_access_amount;
+----------------------+------------------------+-------------------------+--+
| t_access_amount.uid  | t_access_amount.month  | t_access_amount.amount  |
+----------------------+------------------------+-------------------------+--+
| A                    | 2015-01                | 33                      |
| A                    | 2015-02                | 10                      |
| A                    | 2015-03                | 20                      |
| B                    | 2015-01                | 30                      |
| B                    | 2015-02                | 15                      |
| B                    | 2015-03                | 45                      |
| C                    | 2015-01                | 30                      |
| C                    | 2015-02                | 40                      |
| C                    | 2015-03                | 30                      |
+----------------------+------------------------+-------------------------+--+

-- 需求:求出每個人截止到每個月的總額

select uid,month,amount,
sum(amount) over(partition by uid order by month rows between unbounded preceding and current row) as accumulate
from t_access_amount; 

 

7.2.  自定義函數

7.2.1.  需求

需要對json數據表中的json數據寫一個自定義函數,用於傳入一個json,返回一個數據值的數組

 

json原始數據表:

有如下json數據:rating.json
{"movie":"1193","rate":"5","timeStamp":"978300760","uid":"1"}
{"movie":"661","rate":"3","timeStamp":"978302109","uid":"1"}
{"movie":"914","rate":"3","timeStamp":"978301968","uid":"1"}
{"movie":"3408","rate":"4","timeStamp":"978300275","uid":"1"}

 統計每個人的一共評閱了多少部電影。

解決思路:

  1、常規方式,用mapreduce先對json數據格式進行解析,生成包含對應的字段hdfs文件如下圖所示,利用hive進行映射,形成數據表,之后的便是常規操作。【mapreduce在數據清洗方面,有重要意義】

  

  2、使用自定義函數

  在hive中,可以使用自定義函數的方式,實現上述json數據格式到上圖說是數據格式的轉換。

  可以用hive中的字符串函數,對么一條記錄進行切割,拆分,這樣太過繁瑣,若是hive中有如下,對json解析的函數【實際是有的】,將會方便很多。

select myjson(json,1)

 

 

-- 自定義函數
/*
有如下json數據:rating.json
{"movie":"1193","rate":"5","timeStamp":"978300760","uid":"1"}
{"movie":"661","rate":"3","timeStamp":"978302109","uid":"1"}
{"movie":"914","rate":"3","timeStamp":"978301968","uid":"1"}
{"movie":"3408","rate":"4","timeStamp":"978300275","uid":"1"}

需要導入hive中進行數據分析
*/

-- 建表映射上述數據
create table t_ratingjson(json string);

load data local inpath '/root/hivetest/rating.json' into table t_ratingjson;

想把上面的原始數據變成如下形式:
1193,5,978300760,1
661,3,978302109,1
914,3,978301968,1
3408,4,978300275,1

思路:如果能夠定義一個json解析函數,則很方便了
create table t_rate
as
select myjson(json,1) as movie,cast(myjson(json,2) as int) as rate,myjson(json,3) as ts,myjson(json,4) as uid from t_ratingjson;

解決:
hive中如何定義自己的函數:
1、先寫一個java類(extends UDF,重載方法public C evaluate(A a,B b)),實現你所想要的函數的功能(傳入一個json字符串和一個腳標,返回一個值)
2、將java程序打成jar包,上傳到hive所在的機器
3、在hive命令行中將jar包添加到classpath : 窗口關閉,函數失效。   
        hive>add jar /root/hivetest/myjson.jar;
4、在hive命令中用命令創建一個函數叫做myjson,關聯你所寫的這個java類
        hive> create temporary function myjson as 'cn.edu360.hive.udf.MyJsonParser';

 

 

7.2.2、  實現步驟

1、開發java的UDF類

public class ParseJson extends UDF{
    
    // 重載 :返回值類型 和參數類型及個數,完全由用戶自己決定
    // 本處需求是:給一個字符串,返回一個數組
    public String[] evaluate(String json,int index) {
        
        String[] split = json.split("\"");
        // split[3],split[7],split[11],split[15]
        return split[4*index - 1];

    }
}

 

2、打jar包

在eclipse中使用export即可

3、上傳jar包到運行hive所在的linux機器

4、在hive中創建臨時函數:

  •   hive類環境中添加jar
  •   關聯自定義函數和java類

在hive的提示符中:

hive> add jar /root/jsonparse.jar;

 

然后,在hive的提示符中,創建一個臨時函數:

hive>CREATE  TEMPORARY  FUNCTION  jsonp  AS  'cn.edu360.hdp.hive.ParseJson';

 

5、開發hql語句,利用自定義函數,從原始表中抽取數據插入新表

create table t_rate
as
select myjson(json,1) as movie,cast(myjson(json,2) as int) as rate,myjson(json,3) as ts,myjson(json,4) as uid from t_ratingjson;

 

注:臨時函數只在一次hive會話中有效,重啟會話后就無效

如果需要經常使用該自定義函數,可以考慮創建永久函數

拷貝jar包到hive的類路徑中:

cp wc.jar apps/hive-1.2.1/lib/

 

創建function:

create function pfuncx as 'com.doit.hive.udf.UserInfoParser';

 

刪除函數,包括臨時函數和永久函數:

DROP  TEMPORARY  FUNCTION  [IF  EXISTS] function_name  
DROP FUNCTION[IF EXISTS] function_name 

 

 

8.    綜合查詢案例

8.1. 用hql來做wordcount

有以下文本文件:

hello tom hello jim
hello rose hello tom
tom love rose rose love jim
jim love tom love is what
what is love

需要用hive做wordcount

-- 建表映射
create table t_wc(sentence string);
-- 導入數據
load data local inpath '/root/hivetest/xx.txt' into table t_wc;

 

hql答案:

SELECT word
    ,count(1) as cnts
FROM (
    SELECT explode(split(sentence, ' ')) AS word
    FROM t_wc
    ) tmp
GROUP BY word
order by cnts desc
;

 

8.2. 級聯報表查詢

思路:

  1、傳統sql方法:分組聚合自連接

  2、窗口分析函數

有如下數據:

A,2015-01,5
A,2015-01,15
B,2015-01,5
A,2015-01,8
B,2015-01,25
A,2015-01,5
C,2015-01,10
C,2015-01,20
A,2015-02,4
A,2015-02,6
C,2015-02,30
C,2015-02,10
B,2015-02,10
B,2015-02,5
A,2015-03,14
A,2015-03,6
B,2015-03,20
B,2015-03,25
C,2015-03,10
C,2015-03,20

建表映射

create table t_access_times(username string,month string,counts int)
row format delimited fields terminated by ',';

需要要開發hql腳本,來統計出如下累計報表:

用戶

月份

月總額

累計到當月的總額

A

2015-01

33

33

A

2015-02

10

43

A

2015-03

30

73

B

2015-01

30

30

B

2015-02

15

45

 

 

 

 

分析1:

sql中的最長常見函數無非是

  1、行級別函數:對每一行都執行同樣的操作

  2、聚合函數:對別一組(大於等於1行)的數據進行通向的操作

到目前為止沒有接觸到,下一行的結果需要使用上一行的數據的函數,比如上述的逐月累加求和,其實這可以用窗口分析函數來做

  **、窗口分析函數 

 

解決方案1:

  1、先按照用戶、月份分組聚合求出每個用戶的月總金額;

  2、將上述數據表自身join

  3、篩選出 合適的結果

  4、求和

第2步結果

A 2015-01 33    A 2015-01 33
A 2015-01 33    A 2015-02 10
A 2015-01 33    A 2015-03 30

A 2015-02 10    A 2015-01 33
A 2015-02 10    A 2015-02 10
A 2015-02 10    A 2015-03 30

A 2015-03 30    A 2015-01 33
A 2015-03 30    A 2015-02 10
A 2015-03 30    A 2015-03 30

B 2015-01 30    B 2015-01 30
B 2015-01 30    B 2015-02 15

B 2015-02 15    B 2015-01 30
B 2015-02 15    B 2015-02 15

 

第3步結果,篩掉右側日期大於左側的數據  

A 2015-01 33    A 2015-01 33

A 2015-02 10    A 2015-01 33
A 2015-02 10    A 2015-02 10

A 2015-03 30    A 2015-01 33
A 2015-03 30    A 2015-02 10
A 2015-03 30    A 2015-03 30

B 2015-01 30    B 2015-01 30

B 2015-02 15    B 2015-01 30
B 2015-02 15    B 2015-02 15

 

 

求解過程:

准備數據,導入數據

 

求出沒人每月的總額

 

將總額表自鏈接

 

 篩選數據

 

 

按照右側表的id和month進行分組聚合

 


 

方案2:窗口分析函數 

思想:把若干條數據作為一個窗口進行分析。

over()— 表示一個窗口

現在已經有了按用戶、月份分組聚合的結果。

 

-- 窗口分析函數
-- 窗口的定義:
-- 針對窗口中數據的操作
-- 哪些數據
select uid, month, amount, 
sum (amount) over(partition by uid order by month rows between unbounded preceding and current row) as accumulate
from t_access_amount;

 

 


 

8.3、日新,日活統計

需求描述

假如有一個web系統,每天生成以下日志文件:

2017-09-15號的數據:
192.168.33.6,hunter,2017-09-15 10:30:20,/a
192.168.33.7,hunter,2017-09-15 10:30:26,/b
192.168.33.6,jack,2017-09-15 10:30:27,/a
192.168.33.8,tom,2017-09-15 10:30:28,/b
192.168.33.9,rose,2017-09-15 10:30:30,/b
192.168.33.10,julia,2017-09-15 10:30:40,/c


2017-09-16號的數據:
192.168.33.16,hunter,2017-09-16 10:30:20,/a
192.168.33.18,jerry,2017-09-16 10:30:30,/b
192.168.33.26,jack,2017-09-16 10:30:40,/a
192.168.33.18,polo,2017-09-16 10:30:50,/b
192.168.33.39,nissan,2017-09-16 10:30:53,/b
192.168.33.39,nissan,2017-09-16 10:30:55,/a
192.168.33.39,nissan,2017-09-16 10:30:58,/c
192.168.33.20,ford,2017-09-16 10:30:54,/c



2017-09-17號的數據:
192.168.33.46,hunter,2017-09-17 10:30:21,/a
192.168.43.18,jerry,2017-09-17 10:30:22,/b
192.168.43.26,tom,2017-09-17 10:30:23,/a
192.168.53.18,bmw,2017-09-17 10:30:24,/b
192.168.63.39,benz,2017-09-17 10:30:25,/b
192.168.33.25,haval,2017-09-17 10:30:30,/c
192.168.33.10,julia,2017-09-17 10:30:40,/c


-- 需求:
建立一個表,來存儲每天新增的數據(分區表)
統計每天的活躍用戶(日活)(需要用戶的ip,用戶的賬號,用戶訪問時間最早的一條url和時間)
統計每天的新增用戶(日新)

 

8.3.1、sql開發

  1、建表映射日志數據

create table t_web_log ( ip string, uid string, access_time string, url string )
partitioned by (day string)
row format delimited fields terminated by ',' ;

 

  2、建表保存日活數據

沒有指定行分隔符,默認是\001不可見字符

create table t_user_active_day ( ip string, uid string, first_access string, url stirng partitioned by(day string);

  

  3、導入數據

load data local inpath '/root/hivetest/log.15' into table t_web_log partition ( day='2017-09-15' );
load data local inpath '/root/hivetest/log.16' into table t_web_log partition ( day='2017-09-16' );
load data local inpath '/root/hivetest/log.17' into table t_web_log partition ( day='2017-09-17' );

 

  4、指標統計

  4.1、日活:

  每日的活躍用戶:在該天內,用戶的第一次訪問記錄

192.168.33.6,hunter,2017-09-15 10:30:20,/a,1
192.168.33.7,hunter,2017-09-15 10:30:26,/b,2

 

insert into table t_user_active_day partition(day='2017-09-15')
select ip, uid ,access_time, url
from
(select ip, uid, access_time, url
row_number() over(partition by uid order by access_time) as rn
from t_web_log
where day = '2017-09-15') tmp
where rn=1

 默認分隔符為\001[不可見]

 

 4.2、日新:

  統計每天的新增用戶:

  思路如下:利用當日活躍用戶(日活是一個去重結果)

 

  一、建表 

  1、歷史用戶表

  每個用戶的uid是唯一的,只保存uid就可以。

create table t_user_history(uid string);

 

  2、新增用戶表

 新怎用戶表里保存的數據可以和日活表一樣(活躍用戶表里存的數據,可能一部分是新用戶,一部分是老用戶)—表結構一致,存的數據不一樣而已。

create table t_user_new_day like t_user_active_day;

 

   二、操作

  1、將當日活躍用戶跟歷史用戶表關聯,找出那些在歷史用戶表中尚未不存在的用戶—當日新增用戶

-- 關聯日活表和歷史用戶表
select a.ip, a.uid, a.access_time, a.url, b.uid
from t_user_active_day a
left join t_user_history b on a.uid = b.uid
where a.day = '2017-09-15';

 

  2、求出15號的新用戶,並插入到新用戶表中

-- 求出15號的新用戶,並插入到新用戶表中
insert into table t_user_new_day partition(day='2017-09-15')
select ip, uid, access_time, url
from
(select a.ip, a.uid, a.access_time, a.url, b.uid as b_uid
from t_user_active_day a
left join t_user_history b on a.uid = b.uid
where a.day = '2017-09-15';) tmp
where tmp.b_uid is null;

 

  3、將15號的新用戶插入到歷史表中

-- 將15號的新用戶,插入到歷史用戶表中
insert into table t_user_history
select uid 
from t_user_new_day where day='2017-09-15';

 下圖是維護的歷史用戶表

 

下圖是每日新用戶表

 

 

設計腳本,自動獲取日期,執行

 

設置date日期。

#!/bin/bash
day_str=`date -d '-1 day' +'%Y-%m-%d'`
# 若是沒有配置過hive環境變量
echo "准備處理 $day_str 的數據..."
hive_exec=/root/apps/hive-1.2.1/bin/hive
# 求日活
HQL_user_active_day="
insert into table big24.t_user_active_day partition(day=\"$day_str\")
select ip, uid ,access_time, url
from
(select ip, uid, access_time, url
row_number() over(partition by uid order by access_time) as rn
from big24.t_web_log
where day = \"$day_str\") tmp
where rn=1
"
# 執行
echo "executing sql:"
echo $HQL_user_active_day
$hive_exec -e "$HQL_user_active_day"

# 求日新
HQL_user_new_day="
insert into table big24.t_user_new_day partition(day=\"$day_str\")
select ip, uid, access_time, url
from
(select a.ip, a.uid, a.access_time, a.url, b.uid as b_uid
from big24.t_user_active_day a
left join big24.t_user_history b on a.uid = b.uid
where a.day = \"$day_str\") tmp
where tmp.b_uid is null
"
# 執行
$hive_exec -e "$HQL_user_new_day"

# 插入歷史數據表
HQL_user_new_to_history="
insert into big24.table t_user_history
select uid 
from big24.t_user_new_day where day=\"$day_str\"
"
# 執行
$hive_exec -e "$HQL_user_new_to_history"

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM