HiveSQL 數據操控、查詢語言(DML、DQL)

第一章 DML-Load加載數據

1.1 背景

回想一下,當在Hive中創建好表之後,默認就會在HDFS上創建一個與之對應的文件夾,默認路徑是由參數hive.metastore.warehouse.dir控制,默認值是/user/hive/warehouse

要想讓hive的表和結構化的數據文件產生映射,就需要把文件移到到表對應的文件夾下面,當然,可以在建表的時候使用location語句指定數據文件的路徑。但是不管路徑在哪裏,必須把數據文件移動到對應的路徑下面。

最原始暴力直接的方式就是使用hadoop fs –put等方式將數據移動到路徑下面。

Hive官方推薦使用Load命令將數據加載到表中。

1.2 Loa

在將數據load加載到表中時,Hive不會進行任何轉換。

加載操作是將數據文件移動到與Hive表對應的位置的純複製/移動操作。

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

LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename [PARTITION (partcol1=val1, partcol2=val2 ...)] [INPUTFORMAT 'inputformat' SERDE 'serde'] (3.0 or later)

filepath

filepath表示的待移動數據的路徑,可以引用一個文件(在這種情況下,Hive將文件移動到表中),也可以是一個目錄(在這種情況下,Hive將把該目錄中的所有文件移動到表中)。

相對路徑,例如:project/data1

絕對路徑,例如:/user/hive/project/data1

具有schema的完整URI,例如:hdfs://namenode:9000/user/hive/project/data1

LOCAL

如果指定了LOCAL, load命令將在本地文件系統中查找文件路徑。如果指定了相對路徑,它將相對於用戶的當前工作目錄進行解釋。用戶也可以爲本地文件指定完整的URI-例如:

file:///user/hive/project/data1。

注意,如果對HiveServer2服務運行此命令。這裏的本地文件系統指的是Hiveserver2服務所在機器的本地Linux文件系統,不是Hive客戶端所在的本地文件系統。

如果沒有指定LOCAL關鍵字,如果filepath指向的是一個完整的URI,hive會直接使用這個URI。 否則如果沒有指定schema或者authority,Hive會使用在hadoop配置文件中定義的schema 和 authority,即參數fs.default.name指定的(不出意外,都是HDFS)。

OVERWRITE

如果使用了OVERWRITE關鍵字,則目標表(或者分區)中的內容會被刪除,然後再將 filepath 指向的文件/目錄中的內容添加到表/分區中。

1.3 案例:load加載數據到Hive表

--------練習:Load Data From Local FS or HDFS------
--step1:建表
--建表student_local 用於演示從本地加載數據
create table student_local(num int,name string,sex string,age int,dept string) row format delimited fields terminated by ',';
--建表student_HDFS  用於演示從HDFS加載數據
create external table student_HDFS(num int,name string,sex string,age int,dept string) row format delimited fields terminated by ',';
--建表student_HDFS_p 用於演示從HDFS加載數據到分區表
create table student_HDFS_p(num int,name string,sex string,age int,dept string) partitioned by(country string) row format delimited fields terminated by ',';

--建議使用beeline客戶端 可以顯示出加載過程日誌信息
--step2:加載數據
-- 從本地加載數據  數據位於HS2(node1)本地文件系統  本質是hadoop fs -put上傳操作
LOAD DATA LOCAL INPATH '/root/hivedata/students.txt' INTO TABLE student_local;

--從HDFS加載數據  數據位於HDFS文件系統根目錄下  本質是hadoop fs -mv 移動操作
--先把數據上傳到HDFS上  hadoop fs -put /root/hivedata/students.txt /
LOAD DATA INPATH '/students.txt' INTO TABLE student_HDFS;

----從HDFS加載數據到分區表中並制定分區  數據位於HDFS文件系統根目錄下
--先把數據上傳到HDFS上 hadoop fs -put /root/hivedata/students.txt /
LOAD DATA INPATH '/students.txt' INTO TABLE student_HDFS_p partition(country ="CHina");

 

 

1.4 Hive3.0 Load新特性

Hive 3.0及更高版本中,除了移動複製操作之外,還支持其他加載操作,因爲Hive在內部在某些場合下會將加載重寫爲INSERT AS SELECT
比如,如果表具有分區,則load命令沒有指定分區,則將load轉換爲INSERT AS SELECT,並假定最後一組列爲分區列。如果文件不符合預期的架構,它將引發錯誤。

-------hive 3.0 load命令新特性------------------
CREATE TABLE if not exists tab1 (col1 int, col2 int)
PARTITIONED BY (col3 int)
row format delimited fields terminated by ',';

LOAD DATA LOCAL INPATH '/root/hivedata/tab1.txt' INTO TABLE tab1;
--tab1.txt內容如下
11,22,1
33,44,2

本來加載的時候沒有指定分區,語句是報錯的,但是文件的格式符合表的結構,前兩個是col1,col2,最後一個是分區字段col3,則此時會將load語句轉換成爲insert as select語句。

在Hive3.0中,還支持使用inputformat、SerDe指定任何Hive輸入格式,例如文本,ORC等。

第二章、DML-Insert插入數據

2.1 背景:RDBMS中insert使用(insert+values)

在MySQL這樣的RDBMS中,通常是insert+values的方式來向表插入數據,並且速度很快。這也是RDBMS中插入數據的核心方式。

INSERT INTO table_name ( field1, field2,...fieldN )
VALUES
( value1, value2,...valueN );

假如說對Hive的定位不清,把Hive當成RDBMS來使用,也使用insert+values的方式插入數據,會如何呢?
--hive中insert+values

create table t_test_insert(id int,name string,age int);
insert into table t_test_insert values(1,"allen",18);

    你會發現執行過程非常非常慢,底層是使用MapReduce把數據寫入HDFS的。

    試想一下,如何在Hive中這樣玩,對於大數據分析,海量數據一條條插入是不是非常刺激。因此在Hive中我們通過將數據清洗成爲結構化文件,再Load加載到表中
    但是並不意味着insert語法在Hive中沒有使用地位了,通常在Hive中我們使用insert+select語句。即插入表的數據來自於後續select查詢語句返回的結果

2.2 insert + select

Hive中insert主要是結合select查詢語句使用,將查詢結果插入到表中,例如:

INSERT OVERWRITE TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...) [IF NOT EXISTS]] select_statement1 FROM from_statement;
INSERT INTO TABLE tablename1 [PARTITION (partcol1=val1, partcol2=val2 ...)] select_statement1 FROM from_statement

INSERT OVERWRITE將覆蓋表或分區中的任何現有數據。

需要保證查詢結果列的數目和需要插入數據表格的列數目一致。

如果查詢出來的數據類型和插入表格對應的列數據類型不一致,將會進行轉換,但是不能保證轉換一定成功,轉換失敗的數據將會爲NULL。

--step1:創建一張源表student
drop table if exists student;
create table student(num int,name string,sex string,age int,dept string)
row format delimited
fields terminated by ',';
--加載數據
load data local inpath '/root/hivedata/students.txt' into table student;

--step2:創建一張目標表  只有兩個字段
create table student_from_insert(sno int,sname string);
--使用insert+select插入數據到新表中
insert into table student_from_insert select num,name from student;

select *
from student_insert1;

2.3 multiple inserts多重插入

multiple inserts可以翻譯成爲多次插入,多重插入,核心是:一次掃描,多次插入。其功能也體現出來了就是減少掃描的次數。

------------multiple inserts----------------------
--當前庫下已有一張表student
select * from student;
--創建兩張新表
create table student_insert1(sno int);
create table student_insert2(sname string);
--多重插入
from student
insert overwrite table student_insert1
select num
insert overwrite table student_insert2
select name;

2.4 dynamic partition insert動態分區插入

功能

對於分區表的數據導入加載,最常見最基礎的是通過load命令加載數據。如下:

create table student_HDFS_p(Sno int,Sname string,Sex string,Sage int,Sdept string) 
partitioned by(country string)
row format delimited fields terminated by ','; --注意 分區字段country的值是在導入數據的時候手動指定的 China LOAD DATA INPATH '/students.txt' INTO TABLE student_HDFS_p partition(country ="China");

接下來我們考慮一下性能問題:
假如說現在有全球224個國家的人員名單(每個國家名單單獨一個文件),讓你導入數據到分區表中,不同國家不同分區,如何高效實現?使用load語法導入224次?
再假如,現在有一份名單students.txt,內容如下:

95001,李勇,男,20,CS
95002,劉晨,女,19,IS
95003,王敏,女,22,MA
95004,張立,男,19,IS
95005,劉剛,男,18,MA
95006,孫慶,男,23,CS
95007,易思玲,女,19,MA
95008,李娜,女,18,CS
95009,夢圓圓,女,18,MA
95010,孔小濤,男,19,CS
95011,包小柏,男,18,MA
95012,孫花,女,20,CS
95013,馮偉,男,21,CS
95014,王小麗,女,19,CS
95015,王君,男,18,MA
95016,錢國,男,21,MA
95017,王風娟,女,18,IS
95018,王一,女,19,IS
95019,邢小麗,女,19,IS
95020,趙錢,男,21,IS
95021,週二,男,17,MA
95022,鄭明,男,20,MA

讓你創建一張分區表,根據最後一個字段(選修專業)進行分區,同一個專業的同學分到同一個分區中,如何實現?如果還是load加載手動指定,即使最終可以成功,效率也是極慢的。
爲此,Hive提供了動態分區插入的語法。
所謂動態分區插入指的是:分區的值是由後續的select查詢語句的結果來動態確定的。根據查詢結果自動分區。

配置參數

hive.exec.dynamic.partition

true

需要設置true爲啓用動態分區插入

hive.exec.dynamic.partition.mode

strict

在strict模式下,用戶必須至少指定一個靜態分區,以防用戶意外覆蓋所有分區;在nonstrict模式下,允許所有分區都是動態的


關於嚴格模式、非嚴格模式,演示如下:

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.cnt

--在這裏,country分區將由SELECT子句(即pvs.cnt)的最後一列動態創建。
--而dt分區是手動指定寫死的。
--如果是nonstrict模式下,dt分區也可以動態創建。

案例:動態分區插入

--動態分區插入
--1、首先設置動態分區模式爲非嚴格模式 默認已經開啓了動態分區功能
set hive.exec.dynamic.partition = true;
set hive.exec.dynamic.partition.mode = nonstrict;

--2、當前庫下已有一張表student
select * from student;

--3、創建分區表 以sdept作爲分區字段
--注意:分區字段名不能和表中的字段名重複。
create table student_partition(Sno int,Sname string,Sex string,Sage int) partitioned by(Sdept string);

--4、執行動態分區插入操作
insert into table student_partition partition(Sdept)
select Sno,Sname,Sex,Sage,Sdept from student;
--其中,Sno,Sname,Sex,Sage作爲表的字段內容插入表中
--Sdept作爲分區字段值

最終執行結果如下,可以發現實現了自動分區:

2.5 insert + directory導出數據

Hive支持將select查詢的結果導出成文件存放在文件系統中。語法格式如下:

--標準語法:
INSERT OVERWRITE [LOCAL] DIRECTORY directory1
    [ROW FORMAT row_format] [STORED AS file_format] (Note: Only available starting with Hive 0.11.0)
SELECT ... FROM ...

--Hive extension (multiple inserts):
FROM from_statement
INSERT OVERWRITE [LOCAL] DIRECTORY directory1 select_statement1
[INSERT OVERWRITE [LOCAL] DIRECTORY directory2 select_statement2] ...

--row_format
: DELIMITED [FIELDS TERMINATED BY char [ESCAPED BY char]] [COLLECTION ITEMS TERMINATED BY char]
[MAP KEYS TERMINATED BY char] [LINES TERMINATED BY char]

注意,導出操作是一個OVERWRITE覆蓋操作。慎重。

目錄可以是完整的URI。如果未指定scheme或Authority,則Hive將使用hadoop配置變量fs.default.name中的方案和Authority,該變量指定Namenode URI。

如果使用LOCAL關鍵字,則Hive會將數據寫入本地文件系統上的目錄。

寫入文件系統的數據被序列化爲文本,列之間用^ A隔開,行之間用換行符隔開。如果任何列都不是原始類型,那麼這些列將序列化爲JSON格式。也可以在導出的時候指定分隔符換行符和文件格式。

--當前庫下已有一張表student
select * from student;

--1、導出查詢結果到HDFS指定目錄下
insert overwrite directory '/tmp/hive_export/e1' select * from student;

--2、導出時指定分隔符和文件存儲格式
insert overwrite directory '/tmp/hive_export/e2' row format delimited fields terminated by ','
stored as orc
select * from student;

--3、導出數據到本地文件系統指定目錄下
insert overwrite local directory '/root/hive_export/e1' select * from student;

 

 

 

第三章、Hive Transaction事務

3.1 Hive事務背景知識

Hive本身從設計之初時,就是不支持事務的,因爲Hive的核心目標是將已經存在的結構化數據文件映射成爲表,然後提供基於表的SQL分析處理,是一款面向分析的工具。

並且Hive映射的數據通常存儲於HDFS上,而HDFS是不支持隨機修改文件數據的。

這個定位就意味着在早期的Hive的SQL語法中是沒有update,delete操作的,也就沒有所謂的事務支持了,因爲都是select查詢分析操作。

從Hive0.14版本開始,具有ACID語義的事務(支持INSERT,UPDATE和 DELETE這些用例)已添加到Hive中,以解決以下場景下遇到的問題:

  • 流式傳輸數據。使用如Apache Flume或Apache Kafka之類的工具將數據流式傳輸到現有分區中,但是這會使讀者感到髒讀(也就是說,開始查詢後能看到寫入的數據)。
  • 變化緩慢的維度數據。在典型的星型模式數據倉庫中,維度表隨時間緩慢變化。例如,零售商將開設新商店,需要將其添加到商店表中,或者現有商店可能會更改其平方英尺或某些其他跟蹤的特徵。這些更改導致插入單個記錄或更新記錄(取決於所選策略)。
  • 數據更新。有時發現收集的數據不正確,需要更正。

3.2 Hive事務表侷限性

雖然Hive支持了具有ACID語義的事務,但是在使用起來,並沒有像在MySQL中使用那樣方便,有很多侷限性。原因很簡單,畢竟Hive的設計目標不是爲了支持事務操作,而是支持分析操作,且最終基於HDFS的底層存儲機制使得文件的增加刪除修改操作需要動一些小心思。具體限制如下:

  • 尚不支持BEGIN,COMMIT和ROLLBACK。所有語言操作都是自動提交的。
  • 僅支持ORC文件格式(STORED AS ORC)。
  • 默認情況下事務配置爲關閉。需要配置參數開啓使用
  • 表必須是分桶表(Bucketed)纔可以使用事務功能。外部表無法創建事務表
  • 表參數transactional必須爲true
  • 外部表不能成爲ACID表,不允許從非ACID會話讀取/寫入ACID表。

3.3 案例:創建使用Hive事務表

--Hive中事務表的創建使用
--1、開啓事務配置(可以使用set設置當前session生效 也可以配置在hive-site.xml中)
set hive.support.concurrency = true; --Hive是否支持併發
set hive.enforce.bucketing = true; --從Hive2.0開始不再需要  是否開啓分桶功能
set hive.exec.dynamic.partition.mode = nonstrict; --動態分區模式  非嚴格
set hive.txn.manager = org.apache.hadoop.hive.ql.lockmgr.DbTxnManager; --
set hive.compactor.initiator.on = true; --是否在Metastore實例上運行啓動壓縮合並
set hive.compactor.worker.threads = 1; --在此metastore實例上運行多少個壓縮程序工作線程。

--2、創建Hive事務表
create table trans_student(
    id int,
    name String,
    age int
)clustered by (id) into 2 buckets stored as orc TBLPROPERTIES('transactional'='true');

--3、針對事務表進行insert update delete操作
insert into trans_student (id, name, age)
values (1,"allen",18);

update trans_student
set age = 20
where id = 1;

delete from trans_student where id =1;

select *
from trans_student;

第四章、DML-Update、Delete更新、刪除數據

首先,必須明確,你理解的Hive這款軟件,定位是什麼?是面向事務支持事務的RDBMS?還是面向分析,支持分析的數據倉庫。這很重要。

Hive是基於Hadoop的數據倉庫,面向分析支持分析工具。因此在Hive中常見的操作的就是分析查詢select操作。將已有的結構化數據文件映射成爲表,然後提供SQL分析數據的能力。

因此Hive剛出現的時候是不支持update和delete語法支持的,因爲Hive所處理的數據都是已經存在的結構化文件,加載到hive表中即可。

後續Hive支持了相關的update和delete操作,不過有很多約束。詳見Hive事務的支持。

update操作

--1、開啓事務配置(可以使用set設置當前session生效 也可以配置在hive-site.xml中)
set hive.support.concurrency = true; --Hive是否支持併發
set hive.enforce.bucketing = true; --從Hive2.0開始不再需要  是否開啓分桶功能
set hive.exec.dynamic.partition.mode = nonstrict; --動態分區模式  非嚴格
set hive.txn.manager = org.apache.hadoop.hive.ql.lockmgr.DbTxnManager; --
set hive.compactor.initiator.on = true; --是否在Metastore實例上運行啓動壓縮合並
set hive.compactor.worker.threads = 1; --在此metastore實例上運行多少個壓縮程序工作線程。

--2、創建Hive事務表
create table trans_student(
                              id int,
                              name String,
                              age int
)clustered by (id) into 2 buckets stored as orc TBLPROPERTIES('transactional'='true');

--3、針對事務表進行insert update delete操作
insert into trans_student (id, name, age)
values (1,"allen",18);

select *
from trans_student;

update trans_student
set age = 20
where id = 1;

delete操作

--1、開啓事務配置(可以使用set設置當前session生效 也可以配置在hive-site.xml中)
set hive.support.concurrency = true; --Hive是否支持併發
set hive.enforce.bucketing = true; --從Hive2.0開始不再需要  是否開啓分桶功能
set hive.exec.dynamic.partition.mode = nonstrict; --動態分區模式  非嚴格
set hive.txn.manager = org.apache.hadoop.hive.ql.lockmgr.DbTxnManager; --
set hive.compactor.initiator.on = true; --是否在Metastore實例上運行啓動壓縮合並
set hive.compactor.worker.threads = 1; --在此metastore實例上運行多少個壓縮程序工作線程。

--2、創建Hive事務表
create table trans_student(
                              id int,
                              name String,
                              age int
)clustered by (id) into 2 buckets stored as orc TBLPROPERTIES('transactional'='true');

--3、針對事務表進行insert update delete操作
insert into trans_student (id, name, age)
values (1,"allen",18);

select *
from trans_student;

delete from trans_student where id =1;

第五章、DQL-Select查詢數據

5.1 基礎查詢

語法樹

[WITH CommonTableExpression (, CommonTableExpression)*]
SELECT [ALL | DISTINCT] select_expr, select_expr, ...
  FROM table_reference
  [WHERE where_condition]
  [GROUP BY col_list]
  [ORDER BY col_list]
  [CLUSTER BY col_list
    | [DISTRIBUTE BY col_list] [SORT BY col_list]
  ]
 [LIMIT [offset,] rows]

table_reference指示查詢的輸入。它可以是普通物理表,視圖,join查詢結果或子查詢結果。
表名和列名不區分大小寫。

案例:美國Covid-19新冠select查詢

下面來準備一下語法測試環境,在附件資料中有一份數據文件《us-covid19-counties.dat》,裏面記錄了2021-01-28美國各個縣累計新冠確診病例數和累計死亡病例數。
在Hive中創建表,加載該文件到表中:

--step1:創建普通表t_usa_covid19
drop table itcast.t_usa_covid19;
CREATE TABLE itcast.t_usa_covid19(
       count_date string,
       county string,
       state string,
       fips int,
       cases int,
       deaths int)
row format delimited fields terminated by ",";
--將源數據load加載到t_usa_covid19表對應的路徑下
load data local inpath '/root/hivedata/us-covid19-counties.dat' into table t_usa_covid19;

--step2:創建一張分區表 基於count_date日期,state州進行分區
CREATE TABLE itcast.t_usa_covid19_p(
     county string,
     fips int,
     cases int,
     deaths int)
partitioned by(count_date string,state string)
row format delimited fields terminated by ",";

--step3:使用動態分區插入將數據導入t_usa_covid19_p中
set hive.exec.dynamic.partition.mode = nonstrict;

insert into table t_usa_covid19_p partition (count_date,state)
select county,fips,cases,deaths,count_date,state from t_usa_covid19;

select_expr

每個select_expr表示您要檢索的列。必須至少有一個 select_expr。

--select_expr
--查詢所有字段或者指定字段
select * from t_usa_covid19_p;
select county, cases, deaths from t_usa_covid19_p;

--查詢匹配正則表達式的所有字段
SET hive.support.quoted.identifiers = none; --帶反引號的名稱被解釋爲正則表達式
select `^c.*` from t_usa_covid19_p;
--查詢當前數據庫
select current_database(); --省去from關鍵字
--查詢使用函數
select count(county) from t_usa_covid19_p;

ALL 、DISTINCT

ALL和DISTINCT選項指定是否應返回重複的行。如果沒有給出這些選項,則默認值爲ALL(返回所有匹配的行)。DISTINCT指定從結果集中刪除重複的行。

--ALL DISTINCT
--返回所有匹配的行
select state
from t_usa_covid19_p;
--相當於
select all state
from t_usa_covid19_p;
--返回所有匹配的行 去除重複的結果
select distinct state
from t_usa_covid19_p;
--多個字段distinct 整體去重
select distinct county,state from t_usa_covid19_p;

WHERE

WHERE條件是一個布爾表達式。在WHERE表達式中,您可以使用Hive支持的任何函數和運算符,但聚合函數除外。
從Hive 0.13開始,WHERE子句支持某些類型的子查詢。

select * from t_usa_covid19_p where state ="California" and deaths > 1000;
select * from t_usa_covid19_p where 1 > 2;  -- 1 > 2 返回false
select * from t_usa_covid19_p where 1 = 1;  -- 1 = 1 返回true

--where條件中使用函數 找出州名字母超過10個
select * from t_usa_covid19_p where length(state) >10 ;

--WHERE子句支持子查詢
SELECT *
FROM A
WHERE A.a IN (SELECT foo FROM B);

--where條件中不能使用聚合函數
--報錯 SemanticException:Not yet supported place for UDAF 'sum'
select state,sum(deaths)
from t_usa_covid19_p where sum(deaths) >100 group by state;


那麼爲什麼不能在where子句中使用聚合函數呢?
因爲聚合函數要使用它的前提是結果集已經確定。而where子句還處於“確定”結果集的過程中,因而不能使用聚合函數。

分區查詢、分區裁剪

通常,SELECT查詢將掃描整個表(所謂的全表掃描)。如果使用PARTITIONED BY子句創建的分區表,則在查詢時可以指定分區查詢,減少全表掃描,也叫做分區裁剪。

所謂分區裁剪指的是:對分區表進行查詢時,會檢查WHERE子句或JOIN中的ON子句中是否存在對分區字段的過濾,如果存在,則僅訪問查詢符合條件的分區,即裁剪掉沒必要訪問的分區。

--找出來自加州,累計死亡人數大於1000的縣 state字段就是分區字段 進行分區裁剪 避免全表掃描
select * from t_usa_covid19_p where state ="California" and deaths > 1000;

--多分區裁剪
select * from t_usa_covid19_p where count_date = "2021-01-28" and state ="California" and deaths > 1000;

GROUP BY

GROUP BY 語句用於結合聚合函數,根據一個或多個列對結果集進行分組。需要注意的是,出現在GROUP BY中select_expr的字段:要麼是GROUP BY分組的字段;要麼是被聚合函數應用的字段。原因很簡單,避免出現一個字段多個值的歧義。

分組字段出現select_expr中,一定沒有歧義,因爲就是基於該字段分組的,同一組中必相同;

被聚合函數應用的字段,也沒歧義,因爲聚合函數的本質就是多進一出,最終返回一個結果。

如上圖所示,基於category進行分組,相同顏色的分在同一組中。

在select_expr中,如果出現category字段,則沒有問題,因爲同一組中category值一樣,但是返回day就有問題了,day的結果不一樣。

下面針對t_usa_covid19_p進行演示:

--根據state州進行分組

--SemanticException:Expression not in GROUP BY key 'deaths'
--deaths不是分組字段 報錯
--state是分組字段 可以直接出現在select_expr中
select state,deaths
from t_usa_covid19_p where count_date = "2021-01-28" group by state;

--被聚合函數應用
select state,count(deaths)
from t_usa_covid19_p where count_date = "2021-01-28" group by state;

HAVING

在SQL中增加HAVING子句原因是,WHERE關鍵字無法與聚合函數一起使用。

HAVING子句可以讓我們篩選分組後的各組數據,並且可以在Having中使用聚合函數,因爲此時where,group by已經執行結束,結果集已經確定。

--having
--統計死亡病例數大於10000的州
--where語句中不能使用聚合函數 語法報錯
select state,sum(deaths)
from t_usa_covid19_p
where count_date = "2021-01-28" and sum(deaths) >10000 group by state;

--先where分組前過濾(此處是分區裁剪),再進行group by分組(含聚合), 分組後每個分組結果集確定 再使用having過濾
select state,sum(deaths)
from t_usa_covid19_p
where count_date = "2021-01-28"
group by state
having sum(deaths) > 10000;

--這樣寫更好 即在group by的時候聚合函數已經作用得出結果 having直接引用結果過濾 不需要再單獨計算一次了
select state,sum(deaths) as cnts
from t_usa_covid19_p
where count_date = "2021-01-28"
group by state
having cnts> 10000;

having與where的區別:

  • having是在分組後對數據進行過濾
  • where是在分組前對數據進行過濾
  • having後面可以使用聚合函數
  • where後面不可以使用聚合

LIMIT

LIMIT子句可用於約束SELECT語句返回的行數。

LIMIT接受一個或兩個數字參數,這兩個參數都必須是非負整數常量。

第一個參數指定要返回的第一行的偏移量(從 Hive 2.0.0開始),第二個參數指定要返回的最大行數。當給出單個參數時,它代表最大行數,並且偏移量默認爲0。

--limit
--沒有限制返回2021.1.28 加州的所有記錄
select * from t_usa_covid19_p
where count_date = "2021-01-28" and state ="California";

--返回結果集的前5條
select * from t_usa_covid19_p
where count_date = "2021-01-28" and state ="California"
limit 5;

--返回結果集從第1行開始 共3行
select * from t_usa_covid19_p
where count_date = "2021-01-28" and state ="California"
limit 2,3; --注意 第一個參數偏移量是從0開始的

Hive SQL查詢執行順序

SELECT [ALL | DISTINCT] select_expr, select_expr, ...
  FROM table_reference
  [WHERE where_condition]
  [GROUP BY col_list]
  [ORDER BY col_list]
  [CLUSTER BY col_list
    | [DISTRIBUTE BY col_list] [SORT BY col_list]
  ]
 [LIMIT [offset,] rows]

在查詢過程中執行順序:from>where>group(含聚合)>having>order>select

所以聚合語句(sum,min,max,avg,count)要比having子句優先執行,而where子句在查詢過程中執行優先級別優先於聚合語句(sum,min,max,avg,count)。

結合下面SQL感受一下:

select state,sum(deaths) as cnts
from t_usa_covid19_p
where count_date = "2021-01-28"
group by state
having cnts> 10000;

5.2 高階查詢

ORDER BY

ORDER BY [ASC|DESC]

Hive SQL中的ORDER BY語法類似於SQL語言中的ORDER BY語法。會對輸出的結果進行全局排序,因此底層使用MapReduce引擎執行的時候,只會有一個reducetask執行。也因此,如果輸出的行數太大,會導致需要很長的時間才能完成全局排序。

默認排序順序爲升序(ASC),也可以指定爲DESC降序。

在Hive 2.1.0和更高版本中,支持在“ order by”子句中爲每個列指定null類型結果排序順序。ASC順序的默認空排序順序爲NULLS FIRST,而DESC順序的默認空排序順序爲NULLS LAST。

---order by
--根據字段進行排序
select * from t_usa_covid19_p
where count_date = "2021-01-28"
and state ="California"
order by deaths; --默認asc null first

select * from t_usa_covid19_p
where count_date = "2021-01-28"
and state ="California"
order by deaths desc; --指定desc null last

--強烈建議將LIMIT與ORDER BY一起使用。避免數據集行數過大
--當hive.mapred.mode設置爲strict嚴格模式時,使用不帶LIMIT的ORDER BY時會引發異常。
select * from t_usa_covid19_p
where count_date = "2021-01-28"
  and state ="California"
order by deaths desc
limit 3;

CLUSTER BY

SELECT expression… FROM table CLUSTER BY col_name;

Hive SQL中的CLUSTER BY語法可以指定根據後面的字段將數據分組,每組內再根據這個字段正序排序(不允許指定排序規則),概況起來就是:根據同一個字段,分且排序。
分組的規則hash散列。hash_func(col_name) % reduce task nums
分爲幾組取決於reduce task的個數。下面在Hive beeline客戶端中針對student表進行演示。

--cluster by
select * from student;
--不指定reduce task個數
--日誌顯示:Number of reduce tasks not specified. Estimated from input data size: 1
select * from student cluster by sno;

--手動設置reduce task個數
set mapreduce.job.reduces =2;
select * from student cluster by sno;

默認情況下,reduce task的個數由Hive在編譯期間自己決定。

設置set mapreduce.job.reduces =2;

 

 

 執行結果如下:分爲兩個部分,每個部分內正序排序。

 假如說,現在想法如下:把學生表數據根據性別分爲兩個部分,每個分組內根據年齡的倒序排序。你會發現CLUSTER BY無法完成了。而order by更不能在這裏使用,因爲它是全局排序,一旦使用order by,編譯期間就會強制把reduce task個數設置爲1。無法滿足分組的需求。

DISTRIBUTE BY +SORT BY

如果說CLUSTER BY的功能是分且排序(同一個字段),那麼DISTRIBUTE BY +SORT BY就相當於把cluster by的功能一分爲二:DISTRIBUTE BY負責分,SORT BY負責分組內排序,並且可以是不同的字段。如果DISTRIBUTE BY +SORT BY的字段一樣,可以得出下列結論:
CLUSTER BY=DISTRIBUTE BY +SORT BY(字段一樣)

--案例:把學生表數據根據性別分爲兩個部分,每個分組內根據年齡的倒序排序。
select * from student distribute by sex sort by sage desc;

--下面兩個語句執行結果一樣
select * from student distribute by sno sort by sno;
select * from student cluster by sno;

總結

  • order by會對輸入做全局排序,因此只有一個reducer,會導致當輸入規模較大時,需要較長的計算時間。
  • sort by不是全局排序,其在數據進入reducer前完成排序。因此,如果用sort by進行排序,並且設置mapred.reduce.tasks>1,則sort by只保證每個reducer的輸出有序,不保證全局有序。
  • distribute by(字段)根據指定字段將數據分到不同的reducer,分發算法是hash散列。
  • Cluster by(字段) 除了具有Distribute by的功能外,還會對該字段進行排序。

如果distribute和sort的字段是同一個時,此時,cluster by = distribute by + sort by

Union聯合查詢

UNION用於將來自多個SELECT語句的結果合併爲一個結果集。語法如下:

select_statement UNION [ALL | DISTINCT] select_statement UNION [ALL | DISTINCT] select_statement ...
  • 使用DISTINCT關鍵字與只使用UNION默認值效果一樣,都會刪除重複行。
  • 使用ALL關鍵字,不會刪除重複行,結果集包括所有SELECT語句的匹配行(包括重複行)。
  • 1.2.0之前的Hive版本僅支持UNION ALL,在這種情況下不會消除重複的行。
  • 每個select_statement返回的列的數量和名稱必須相同
--union
--使用DISTINCT關鍵字與使用UNION默認值效果一樣,都會刪除重複行。
select num,name from student_local
UNION
select num,name from student_hdfs;
--和上面一樣
select num,name from student_local
UNION DISTINCT
select num,name from student_hdfs;

--使用ALL關鍵字會保留重複行。
select num,name from student_local
UNION ALL
select num,name from student_hdfs;

--如果要將ORDER BY,SORT BY,CLUSTER BY,DISTRIBUTE BY或LIMIT應用於單個SELECT
--請將子句放在括住SELECT的括號內
SELECT sno,sname FROM (select sno,sname from student_local LIMIT 2) subq1
UNION
SELECT sno,sname FROM (select sno,sname from student_hdfs LIMIT 3) subq2

--如果要將ORDER BY,SORT BY,CLUSTER BY,DISTRIBUTE BY或LIMIT子句應用於整個UNION結果
--請將ORDER BY,SORT BY,CLUSTER BY,DISTRIBUTE BY或LIMIT放在最後一個之後。
select sno,sname from student_local
UNION
select sno,sname from student_hdfs
order by sno desc;

Subqueries子查詢

from子句中子查詢

在Hive0.12版本,僅在FROM子句中支持子查詢。而且必須要給子查詢一個名稱,因爲FROM子句中的每個表都必須有一個名稱。

子查詢返回結果中的列必須具有唯一的名稱。子查詢返回結果中的列在外部查詢中可用,就像真實表的列一樣。子查詢也可以是帶有UNION的查詢表達式。Hive支持任意級別的子查詢,也就是所謂的嵌套子查詢。

Hive 0.13.0和更高版本中的子查詢名稱之前可以包含可選關鍵字“ AS” 。

--from子句中子查詢(Subqueries)
--子查詢
SELECT num
FROM (
         select num,name from student_local
     ) tmp;

--包含UNION ALL的子查詢的示例
SELECT t3.name
FROM (
         select num,name from student_local
         UNION distinct
         select num,name from student_hdfs
     ) t3;

where子句中子查詢

從Hive 0.13開始,WHERE子句支持某些類型的子查詢。

--where子句中子查詢(Subqueries)
--不相關子查詢,相當於IN、NOT IN,子查詢只能選擇一個列。
--(1)執行子查詢,其結果不被顯示,而是傳遞給外部查詢,作爲外部查詢的條件使用。
--(2)執行外部查詢,並顯示整個結果。  
SELECT *
FROM student_hdfs
WHERE student_hdfs.num IN (select num from student_local limit 2);

--相關子查詢,指EXISTS和NOT EXISTS子查詢
--子查詢的WHERE子句中支持對父查詢的引用
SELECT A
FROM T1
WHERE EXISTS (SELECT B FROM T2 WHERE T1.X = T2.Y);

Common Table Expressions(CTE)

CTE介紹

公用表表達式(CTE)是一個臨時結果集,該結果集是從WITH子句中指定的簡單查詢派生而來的,該查詢緊接在SELECT或INSERT關鍵字之前。

CTE僅在單個語句的執行範圍內定義。一個或多個CTE可以在Hive SELECT,INSERT,  CREATE TABLE AS SELECT或CREATE VIEW AS SELECT語句中使用。

CTE案例

--選擇語句中的CTE
with q1 as (select sno,sname,sage from student where sno = 95002)
select *
from q1;

-- from風格
with q1 as (select sno,sname,sage from student where sno = 95002)
from q1
select *;

-- chaining CTEs 鏈式
with q1 as ( select * from student where sno = 95002),
     q2 as ( select sno,sname,sage from q1)
select * from (select sno from q2) a;


-- union案例
with q1 as (select * from student where sno = 95002),
     q2 as (select * from student where sno = 95004)
select * from q1 union all select * from q2;

--視圖,CTAS和插入語句中的CTE
-- insert
create table s1 like student;

with q1 as ( select * from student where sno = 95002)
from q1
insert overwrite table s1
select *;

select * from s1;

-- ctas
create table s2 as
with q1 as ( select * from student where sno = 95002)
select * from q1;

-- view
create view v1 as
with q1 as ( select * from student where sno = 95002)
select * from q1;

select * from v1;

第六章、join連接查詢

6.1 join概念回顧

根據數據庫的三範式設計要求和日常工作習慣來說,我們通常不會設計一張大表把所有類型的數據都放在一起,而是不同類型的數據設計不同的表存儲。比如在設計一個訂單數據表的時候,可以將客戶編號作爲一個外鍵和訂單表建立相應的關係。而不可以在訂單表中添加關於客戶其它信息(比如姓名、所屬公司等)的字段。

在這種情況下,有時需要基於多張表查詢才能得到最終完整的結果,SQL中join語法的出現是用於根據兩個或多個表中的列之間的關係,從這些表中共同組合查詢數據,因此有時爲了得到完整的結果,我們就需要執行 join。

Hive作爲面向分析的數據倉庫軟件,爲了更好的支持數據分析的功能豐富,也實現了join的語法,整體上來看和RDBMS中的join語法類似,只不過在某些點有自己的特色。需要特別注意。

6.2 Hive join語法

在Hive中,當下版本3.1.2總共支持6種join語法。分別是:
inner join(內連接)、left join(左連接)、right join(右連接)、full outer join(全外連接)、left semi join(左半開連接)、cross join(交叉連接,也叫做笛卡爾乘積)。

規則樹

join_table:
    table_reference [INNER] JOIN table_factor [join_condition]
  | table_reference {LEFT|RIGHT|FULL} [OUTER] JOIN table_reference join_condition
  | table_reference LEFT SEMI JOIN table_reference join_condition
  | table_reference CROSS JOIN table_reference [join_condition] (as of Hive 0.10)

table_reference:
    table_factor
  | join_table

table_factor:
    tbl_name [alias]
  | table_subquery alias
  | ( table_references )

join_condition:
    ON expression
  • table_reference:是join查詢中使用的表名,也可以是子查詢別名(查詢結果當成表參與join)。
  • table_factor:與table_reference相同,是聯接查詢中使用的表名,也可以是子查詢別名。
  • join_condition:join查詢關聯的條件, 如果在兩個以上的表上需要連接,則使用AND關鍵字。

語法豐富

Hive中join語法從面世開始其實並不豐富,不像在RDBMS中那麼靈活,很多早期接觸Hive的用戶在使用join的時候,一個最大的感受就是不支持不相等連接。

從Hive 0.13.0開始,支持隱式聯接表示法(請參閱HIVE-5558)。這允許FROM子句連接以逗號分隔的表列表,而省略JOIN關鍵字。例如:

SELECT *
FROM table1 t1, table2 t2, table3 t3
WHERE t1.id = t2.id AND t2.id = t3.id AND t1.zipcode = '02535';

從Hive 2.2.0開始,支持ON子句中的複雜表達式,支持不相等連接(請參閱HIVE-15211和HIVE-15251)。在此之前,Hive不支持不是相等條件的聯接條件。

SELECT a.* FROM a JOIN b ON (a.id = b.id)
SELECT a.* FROM a JOIN b ON (a.id = b.id AND a.department = b.department)
SELECT a.* FROM a LEFT OUTER JOIN b ON (a.id <> b.id)

6.3 join查詢數據環境準備

爲了更好的練習、學習掌握Hive中的join語法,下面我們去創建3張表並且加載數據到表中。
表1:employee 員工表;
表2:employee_address 員工住址信息表;
表3:employee_connection 員工聯繫方式表;

--table1: 員工表
CREATE TABLE employee(
   id int,
   name string,
   deg string,
   salary int,
   dept string
 ) row format delimited
fields terminated by ',';

--table2:員工住址信息表
CREATE TABLE employee_address (
    id int,
    hno string,
    street string,
    city string
) row format delimited
fields terminated by ',';

--table3:員工聯繫方式表
CREATE TABLE employee_connection (
    id int,
    phno string,
    email string
) row format delimited
fields terminated by ',';
--加載數據到表中
load data local inpath '/root/hivedata/employee.txt' into table employee;
load data local inpath '/root/hivedata/employee_address.txt' into table employee_address;
load data local inpath '/root/hivedata/employee_connection.txt' into table employee_connection;

 

 

6.4 Hive inner join

內連接是最常見的一種連接,它也被稱爲普通連接,而關係模型提出者E.FCodd(埃德加•科德)最早稱之爲自然連接。其中inner可以省略。inner join == join 等價於早期的連接語法。

內連接,只有進行連接的兩個表中都存在與連接條件相匹配的數據纔會被留下來

--1、inner join
select e.id,e.name,e_a.city,e_a.street
from employee e inner join employee_address e_a
on e.id =e_a.id;
--等價於 inner join=join
select e.id,e.name,e_a.city,e_a.street
from employee e join employee_address e_a
on e.id =e_a.id;

--等價於 隱式連接表示法
select e.id,e.name,e_a.city,e_a.street
from employee e , employee_address e_a
where e.id =e_a.id;

6.5 Hive left join

left join中文叫做是左外連接(Left Outer Jion)或者左連接,其中outer可以省略,left outer join是早期的寫法。
left join的核心就在於left左。左指的是join關鍵字左邊的表,簡稱左表。
通俗解釋:join時以左表的全部數據爲準,右邊與之關聯;左表數據全部返回,右表關聯上的顯示返回,關聯不上的顯示null返回。

--2、left join
select e.id,e.name,e_conn.phno,e_conn.email
from employee e left join employee_connection e_conn
on e.id =e_conn.id;

--等價於 left outer join
select e.id,e.name,e_conn.phno,e_conn.email
from employee e left outer join  employee_connection e_conn
on e.id =e_conn.id;

6.6 Hive right join

right join中文叫做是右外連接(Right Outer Jion)或者右連接,其中outer可以省略。

right join的核心就在於Right右。右指的是join關鍵字右邊的表,簡稱右表。

通俗解釋:join時以右表的全部數據爲準,左邊與之關聯;右表數據全部返回,左表關聯上的顯示返回,關聯不上的顯示null返回。

很明顯,right join和left join之間很相似,重點在於以哪邊爲準,也就是一個方向的問題。

--3、right join
select e.id,e.name,e_conn.phno,e_conn.email
from employee e right join employee_connection e_conn
on e.id =e_conn.id;

--等價於 right outer join
select e.id,e.name,e_conn.phno,e_conn.email
from employee e right outer join employee_connection e_conn
on e.id =e_conn.id;

6.7 Hive full outer join

full outer join 等價 full join  ,中文叫做全外連接或者外連接
包含左、右兩個表的全部行,不管另外一邊的表中是否存在與它們匹配的行  在功能上,它等價於對這兩個數據集合分別進行左外連接和右外連接,然後再使用消去重複行的操作將上述兩個結果集合併爲一個結果集。

--4、full outer join
select e.id,e.name,e_a.city,e_a.street
from employee e full outer join employee_address e_a
on e.id =e_a.id;
--等價於
select e.id,e.name,e_a.city,e_a.street
from employee e full  join employee_address e_a
on e.id =e_a.id;

6.8 Hive left semi join

左半開連接(LEFT SEMI JOIN)會返回左邊表的記錄,前提是其記錄對於右邊的表滿足ON語句中的判定條件。

從效果上來看有點像inner join之後只返回左表的結果。

--5、left semi join
select *
from employee e left semi join employee_address e_addr
on e.id =e_addr.id;

--相當於 inner join 只不過效率高一些
select e.*
from employee e inner join employee_address e_addr
on e.id =e_addr.id;

6.9 Hive cross join

交叉連接cross join,將會返回被連接的兩個表的笛卡爾積,返回結果的行數等於兩個錶行數的乘積。對於大表來說,cross join慎用。

在SQL標準中定義的cross join就是無條件的inner join。返回兩個表的笛卡爾積,無需指定關聯鍵。

在HiveSQL語法中,cross join 後面可以跟where子句進行過濾,或者on條件過濾。

--6、cross join
--下列A、B、C 執行結果相同,但是效率不一樣:
--A:
select a.*,b.* from employee a,employee_address b where a.id=b.id;
--B:
select * from employee a cross join employee_address b on a.id=b.id;
select * from employee a cross join employee_address b where a.id=b.id;

--C:
select * from employee a inner join employee_address b on a.id=b.id;

--一般不建議使用方法A和B,因爲如果有WHERE子句的話,往往會先進行笛卡爾積返回數據然後才根據WHERE條件從中選擇。
--因此,如果兩個表太大,將會非常非常慢,不建議使用。

6.10 Hive join使用注意事項

總體來說,隨着Hive的版本發展,join語法的功能也愈加豐富。當下我們課程使用的是3.1.2版本,有以下幾點需要注意:

a) 允許使用複雜的聯接表達式

SELECT a.* FROM a JOIN b ON (a.id = b.id)
SELECT a.* FROM a JOIN b ON (a.id = b.id AND a.department = b.department)
SELECT a.* FROM a LEFT OUTER JOIN b ON (a.id <> b.id)

b) 同一查詢中可以連接2個以上的表

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

c) 如果每個表在聯接子句中使用相同的列,則Hive將多個表上的聯接轉換爲單個MR作業

SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)
--由於聯接中僅涉及b的key1列,因此被轉換爲1個MR作業來執行
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2)
--會轉換爲兩個MR作業,因爲在第一個連接條件中使用了b中的key1列,而在第二個連接條件中使用了b中的key2列。第一個map / reduce作業將a與b聯接在一起,然後將結果與c聯接到第二個map / reduce作業中。

d) join時的最後一個表會通過reducer流式傳輸,並在其中緩衝之前的其他表,因此,將大表放置在最後有助於減少reducer階段緩存數據所需要的內存

SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)
--由於聯接中僅涉及b的key1列,因此被轉換爲1個MR作業來執行,並且表a和b的鍵的特定值的值被緩衝在reducer的內存中。然後,對於從c中檢索的每一行,將使用緩衝的行來計算聯接。
SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2)
--計算涉及兩個MR作業。其中的第一個將a與b連接起來,並緩衝a的值,同時在reducer中流式傳輸b的值。
在第二個MR作業中,將緩衝第一個連接的結果,同時將c的值通過reducer流式傳輸。

e) 在join的時候,可以通過語法STREAMTABLE提示指定要流式傳輸的表。如果省略STREAMTABLE提示,則Hive將流式傳輸最右邊的表。

SELECT /*+ STREAMTABLE(a) */ a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)
--a,b,c三個表都在一個MR作業中聯接,並且表b和c的鍵的特定值的值被緩衝在reducer的內存中。然後,對於從a中檢索到的每一行,將使用緩衝的行來計算聯接。如果省略STREAMTABLE提示,則Hive將流式傳輸最右邊的表。

f) join在WHERE條件之前進行。

g) 如果除一個要連接的表之外的所有表都很小,則可以將其作爲僅map作業執行

SELECT /*+ MAPJOIN(b) */ a.key, a.value FROM a JOIN b ON a.key = b.key
--不需要reducer。對於A的每個Mapper,B都會被完全讀取。限制是不能執行FULL / RIGHT OUTER JOIN b。

還有一些其他相關的使用注意事項,可以參考官方
https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Joins

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章