主要內容:
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_HOME 和HIVE_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 代表:標准輸出
> 代表:重定向
2 代表:錯誤輸出
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. 字符串類型
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;
過濾數據用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"