【Hive】SQL語句大全

庫操作

創建數據庫

-- 創建一個數據庫,在HDFS上的默認路徑爲/user/hive/warehouse/*.db
create database mydatabase;
-- 可以使用if exists判斷數據庫是否已存在(存在則不創建)
create database if not exists mydatabase;
-- 創建一個數據庫,並指定其存放路徑
create database mydatabase location '/mydatabase.db'; 
-- 創建一個數據庫,指定一個已存在的文件夾(my)作爲數據庫內容的存放位置
create database mydatabase location '/databases/my/';

查詢數據庫

-- 顯示所有數據庫
show databases;
-- 模糊搜索
show databases like 'my*';
-- 查看信息
desc database mydatabase;
-- 查看詳細信息
desc database extended mydatabase;
-- 切換當前數據庫
use mydatabase;

修改數據庫

可以修改一些其他的附加信息,不能修改元數據信息

-- 給數據庫添加信息
alter database mydatabase set dbproperties('createtime'='202003');
-- 查看上述添加的信息
desc database extended mydatabase;

刪除數據庫

-- 刪除一個空的數據庫
drop database mydatabase;
-- 最好使用if exists判斷數據庫是否存在
drop database if exists mydatabase;
-- 如果數據庫不爲空,可以採用cascade命令強制刪除
drop database mydatabase cascade;

表操作

創建表

-- 創建一張表
create table student(id int, name string);
-- 創建一張外部表
create external table student(id int, name string);
-- 創建表並設置表中數據的分隔符(以製表符爲例)
create table student(id int, name string) 
row format delimited fields terminated by '\t';
-- 創建表並設置表中數組數據的分隔符(以製表符爲例)
create table student(id int, name string) 
collection items terminated by "\t" ;

查看錶

-- 查看當前數據庫中的所有表
show tables;

修改表

-- 重命名錶
alter table student rename to new_student;
-- 添加列(添加的列在分區字段之前,括號中使用逗號分隔添加多列)
alter table student add columns(gender string);
-- 更新列信息(舊字段名,新字段名,新字段類型都要寫)
alter table student change column name name2 string;
-- 替換表中所有字段(將所有字段合併替換爲一個字段)
alter table student replace columns(replace string);

刪除表

drop table student;

內部表(管理表)和外部表

兩者的區別

刪除時,內部表把元數據和具體數據都刪除,而外部表只刪除元數據。

互相轉換

注意:這裏區分大小寫,括號中的內容要大寫!

如果不是大寫,該屬性會變成普通的附加屬性。

-- 轉爲外部表
alter table student set tblproperties('EXTERNAL'='TRUE');
-- 轉爲內部表
alter table student set tblproperties('EXTERNAL'='FALSE');

分區表

分區在HDFS上對應一個獨立的文件夾,屬於元數據,但用法相當於一個字段,可以用來過濾

創建分區表

-- 創建一個表,並設置以"month"字段分區
create table student(id int, name string) 
partitioned by(month string);
-- 創建二級分區表
create table student(id int, name string)
partitioned by(month string, day string)

添加分區

-- 往分區表裏添加一個分區
alter table student add partition(month='202003');
-- 往分區表裏添加多個分區(以空格隔開)
alter table student add partition(month='202003') partition(month='202003');

往分區表中添加數據

-- 加上關鍵字partition(...)指定分區即可;如果沒有該分區,則自動新建
load data local inpath'/opt/file.txt' into student partition(month='202003');
insert into student partition(month='202003') values(1,'abc');

查詢分區表數據

-- 通過分區查找數據
select * from student where month='202003';
select * from student where month='202003' and day='01';

刪除分區

-- 刪除一個分區表裏的分區
alter table student drop partition(month='202003');
-- 刪除多個分區表裏的分區(以逗號隔開)
alter table student drop partition(month='202003'),partition(month='202003');

查看分區

-- 顯示所有分區
show partitions student;

修復分區

如果數據是通過HDFS直接上傳到分區目錄,如果分區沒定義,則會查詢不到剛上傳的數據

-- 修復命令
msck repair table student;
-- 也可以直接讓此目錄成爲分區目錄(這裏以month='20200316'爲例)
alter table student add partition(month='20200316');

數據操作

數據導入

Load導入

-- 本地文件導入Hive表
load data local inputpath '/opt/student.txt' into table student;
-- HDFS文件導入Hive表(相當於移動文件到數據庫所在的文件夾)
load data inputpath '/student.txt' into table student;
-- 也可以直接移動文件至HDFS中的Hive表目錄下
hadoop fs -put student.txt /user/hive/warehouse/student
-- 導入,並覆蓋所有表中數據
load data local inputpath '/opt/student.txt' overwrite into table student;
-- 建表時通過Location指定加載數據路徑(文件夾)
create table student(id int, name string)
row format delimited fields terminated by '\t'
location '/dir';

Insert插入

-- 直接添加一條記錄
insert into table student values(1,'abc');
-- 添加,並覆蓋所有表中數據
insert overwrite table student values(1,'abc');

Import導入

只能導入被export導出的文件

-- 通過import導入數據
import table student2 from '/export/student';

數據查詢

基本查詢

-- 查詢表中所有數據
select * from student;
-- 查詢表中指定列數據
select id, name from student;
-- 將查詢到的結果插入到其他表
insert into student2 select * from student;
-- 以查詢到的結果創建新表
create table student2 as select id, name from student;
-- 以列別名顯示(as可不寫),使用別名還可以提升性能
select id as sid, name as sname from student;
-- 將查詢到的id值加100後顯示
select id+100 from student;
-- 常用函數(計數:count, 最大值:max, 最小值:min, 求和:sum, 平均數:avg)
select count(*) from student;
-- Limit語句用於限制返回的行數
select * from student limit 3;
-- Where語句用於過濾
select * from student where id = 1;
Floor 取整
-- 對123.123取整,結果爲123
select floor(123.123)
Like 和 Rlike

like: 選擇類似的值

% 代表零個或多個字符(任意個字符)。
_ 代表一個字符。

rlike: Java的正則匹配

-- 查詢姓“小”開頭的學生
select * from student where name like '小%';
-- 查詢姓名以“小”開頭的學生,並且名字只有兩個字
select * from student where name like '小_';
-- 查詢age字段中只包含數字的那些記錄
select * from student where age rlike '\\d+';
Distinct 去重

會將數據放入同一個Reducer,可能會報內存溢出,數據量大時慎用

-- 無論這個年齡段人數有多少,去重後只顯示1個
select age,count(distinct age) from mydatabase.student group by age;
Group By 分組查詢
-- 以字段age分組,配合count使用顯示每組的個數
select age,count(*) from student group by age;
-- 以字段grade分組,配合avg使用顯示每組age的平均數
select grade,avg(age) from student group by grade;
-- 先以age分組,再以gender分組,統計每個年齡段男女人數
select count(gender) from student group by age,gender;
Having 語句

where:對錶中的列發揮作用,不可跟聚合函數

having:對查詢結果中的列發揮作用,相當於二次篩選,可跟聚合函數,只能用於group byf分組統計語句

-- 以字段grade分組,顯示age平均值大於18的grade
select grade from student group by grade having avg(age)>18;
Join 語句

只支持等值連接,不支持非等值連接

-- 假設有兩張表:dept部門表和employee員工表
-- 內連接(只有都存在的數據纔會顯示)
-- 查詢員工表和部門表編號相同的數據,並顯示員工名字和部門名稱
select employee.name,dept.name from employee join dept on dept.d_id=employee.d_id;
-- 左外連接(顯示所有左表中有的數據)
select employee.name,dept.name from employee left join dept on dept.d_id=employee.d_id;
-- 右外連接(顯示所有右表中有的數據)
select employee.name,dept.name from employee right join dept on dept.d_id=employee.d_id;
-- 滿外連接(顯示所有數據,不匹配的值使用NULL值代替)
select employee.name,dept.name from employee full join dept on dept.d_id=employee.d_id;

常用查詢函數

NVL 空字段賦值

NVL(string1, replace_with)

如果string1爲NULL,該函數返回replace_with的值,否則返回string1的值

-- 如果age爲null,用18代替
select nvl(age,18) from student;
-- 替換的參數可以是字段,如果age爲null,用id值代替
select nvl(age,id) from student;
時間類
Date_format

格式化時間 ,注意:只能匹配橫杆 "-"

select date_format('2020-03-19','yyyy-MM-dd HH:mm:ss');
-- 結果: 2020-03-19 00:00:00
Date_add

時間跟天數相加,天數可以爲負

select date_add('2020-03-19', 10); 
-- 結果: 2020-03-29
Date_sub

時間跟天數相減,天數可以爲負

select date_sub('2020-03-19', 10);
-- 結果: 2020-03-09
Datediff

兩個時間相減,結果爲天數,注意:是參數1 - 參數2

時分秒不影響最後的結果

select datediff('2020-03-19', '2020-03-29');
-- 結果: -10
select datediff('2020-03-29', '2020-03-19');
-- 結果: 10
select datediff('2020-03-29 13:13:13','2020-03-19 12:12:12');
-- 結果: 10
CASE WHEN 語句
-- 判斷,如果gender爲'男'或'女',分別設置1,最後統計每個年齡段男女人數
select
age,
sum(case gender when '男' then 1 else 0 end) male_count,
sum(case gender when '女' then 1 else 0 end) female_count
from student group by age;
IF 語句
-- 以下代碼等價於上面的case when
select
age,
sum(if(gender='男',1,0)) male_count,
sum(if(gender='女',1,0)) female_count
from student group by age;
行轉列
Concat

concat(string1/col, string2/col, …)

輸入任意個字符串(或字段,可以爲int類型等),返回拼接後的結果

select concat(id,'-',name,'-',age) from student;
Concat_ws

concat_ws(separator, str1, str2, …)

特殊形式的concat(),參數只能爲字符串,第一個參數爲後面參數的分隔符

select concat_ws('-', name, gender) from student;
Collect_set

collect_set(col)

將某字段進行去重處理,返回array類型;該函數只接受基本數據類型

select collect_set(age) from student;
列轉行
Explode

explode(col)

將一列中複雜的array或map結構拆分成多行

-- 將上面collect_set後的結果使用explode拆分
select explode(ages)
from (select collect_set(age) as ages from student ) as n1;
Lateral View

LATERAL VIEW udtf(expression) tableAlias AS columnAlias

配合split, explode等UDTF一起使用,它能夠將一列數據拆成多行數據,並且對拆分後的結果進行聚合

-- 假設有如下movies表,字段名分別爲movie(string)和category(array<string>)
-- movie	category
--《疑犯追蹤》	懸疑,動作,科幻,劇情
--《海豹突擊隊》	動作,劇情,罪案
--《戰狼2》	戰爭,動作,災難
select movie, cate
from movies
lateral view explode(category) tmp_table as cate;
-- 結果:
--《疑犯追蹤》	懸疑
--《疑犯追蹤》	動作
--《疑犯追蹤》	科幻
--《疑犯追蹤》	劇情
--《海豹突擊隊》	動作
-- ...
窗口函數

OVER():指定分析函數工作的數據窗口大小,這個數據窗口大小可能會隨着行的變化而變化,

注意:該函數會對結果數據產生影響(比如在over(order by id)中排序後,結果也會被排序)

CURRENT ROW:當前行;
n PRECEDING:往前 n 行數據;
n FOLLOWING:往後 n 行數據;
UNBOUNDED:起點,UNBOUNDED PRECEDING 表示從前面的起點, UNBOUNDED FOLLOWING 表示到後面的終點;
LAG(col,n):往前第 n 行數據;
LEAD(col,n):往後第 n 行數據;
NTILE(n):把有序分區中的行分發到指定數據的組中,各個組有編號,編號從 1 開始,
對於每一行,NTILE 返回此行所屬的組的編號。注意:n 必須爲 int 類型。

-- 幾個參數的固定格式寫法
-- 計算從當前行開始計算[2,4]行的gender數量
select *,count(gender) over(rows between 2 following and 4 following) from student;

假設有如下business表

name orderdate cost
------------------
jack,2017-01-01,10
tony,2017-01-02,15
jack,2017-02-03,23
tony,2017-01-04,29
jack,2017-01-05,46
jack,2017-04-06,42
tony,2017-01-07,50
jack,2017-01-08,55
mart,2017-04-08,62
mart,2017-04-09,68
neil,2017-05-10,12
mart,2017-04-11,75
neil,2017-06-12,80
mart,2017-04-13,94
------------------
需求 
(1)查詢在 2017 年 4 月份購買過的顧客及總人數 
(2)查詢顧客的購買明細及月購買總額 
(3)上述的場景,要將 cost 按照日期進行累加 
(4)上述的場景,分別累加每個用戶每個月的開銷
(5)查詢顧客上次的購買時間 
(6)查詢前20%時間的訂單信息
  1. 查詢在 2017 年 4 月份購買過的顧客及總人數

    select 
    name,
    count(*) over() as all_person
    from business
    where date_format(orderdate,'yyyy-MM')='2017-04'
    group by name;
    
  2. 查詢顧客的購買明細及該用戶月購買總額

    select 
    name,
    orderdate,
    date_format(orderdate,'yyyy-MM') this_month,
    cost,
    sum(cost) over(distribute by name, date_format(orderdate,'yyyy-MM')) as this_user_this_month_sum
    from business;
    
  3. 上述的場景,要將 cost 按照日期進行累加

    select 
    name,
    orderdate,
    cost,
    sum(cost) over(distribute by name sort by orderdate)
    from business;
    
  4. 上述的場景,分別累加每個用戶每個月的開銷

    select 
    name,
    orderdate,
    cost,
    sum(cost) over(distribute by name,month(orderdate) sort by day(orderdate))
    from business;
    
  5. 查詢顧客上次的購買時間

    -- lag的第三個參數:如果沒有找到數據,用該參數代替,否則爲NULL
    select
    name,
    orderdate,
    cost,
    lag(orderdate,1,'0000-00-00') over(distribute by name sort by orderdate)
    from business;
    
  6. 查詢前20%時間的訂單信息

    -- 使用ntile函數分組實現該操作
    select * from
    (
        select
        name,
        orderdate,
        cost,
        ntile(5) over(order by orderdate) as sorted
        from business
    ) as tmp_table
    where sorted = 1;
    
Rank 排序

該函數配合OVER()使用

RANK() 排序相同時會重複,總數不會變
DENSE_RANK() 排序相同時會重複,總數會減少
ROW_NUMBER() 會根據順序計算

假設有如下score表

name  subject  score 
--------------------
小明	語文	87
小明	數學	95
小明	英語	68
小綠	語文	94
小綠	數學	56
小綠	英語	84
小紅	語文	64
小紅	數學	86
小紅	英語	84
小藍	語文	65
小藍	數學	85
小藍	英語	78
---------------------
-- 需求:計算每門學科成績排名。 
select
*,
rank() over(distribute by subject sort by score desc),
dense_rank() over(distribute by subject sort by score desc),
row_number() over(distribute by subject sort by score desc)
from score;
Regexp_replace 正則替換

regexp_replace(string A, string B, replace)

將字符串A中的符合JAVA正則表達式B的部分替換爲replace。

注意,在有些情況下要使用轉義字符

-- 將字符串中的“/”替換爲“-”
select regexp_replace('2020/03/21','/','-');
-- 結果:2020-03-21

數據排序

Order By 全局排序

整張表的排序,只有一個Reducer

-- 將數據按id值升序排序(默認升序,可以不寫asc)
select * from student order by id asc;
-- 將數據按id值降序排序
select * from student order by id desc;

Sort By 內部排序

對每個Reducer進行排序,不影響全局結果集

直接使用會將結果平均分配給每個文件(避免數據傾斜)

一般配合Distribute By使用

-- 先設置reduce個數
set mapreduce.job.reduces=3;
-- 直接查看結果,看不出變化
select * from student sort by id;
-- 將排序結果導出到文件
insert overwrite local directory '/opt/datas/sort-out'
select * from student sort by id;

Distribute By 分區排序

類似MapReduce中的Partition分區,一般配合Sort By排序使用

需要分配多個reduce才能看到效果

注意:該語句需要寫在 Sort By 語句之前!

-- 先設置reduce的個數
set mapreduce.job.reduces=3;
-- 先按照id值分區,再按照age值升序排序
insert overwrite local directory '/opt/datas/dis-out'
select * from student distribute by id sort by age;

Cluster By 排序

當 Distribute By 和 Sort By 字段相同時,可以使用 Cluster By 方式

該排序只能是升序排序

-- 以下兩種寫法等價
select * from student cluster by grade;
select * from student distribute by grade sort by grade;

分桶和抽樣查詢

分區針對的是數據的存儲路徑,分桶針對的是數據文件

創建分桶表
-- 創建分桶表
create table studentbucket (id int, name string, age int)
clustered by (id) into 4 buckets
row format delimited fields terminated by '\t';
-- 可以查看錶結構獲取bucket數量
desc formatted studentbucket;

在導入數據之前,要先設置一些屬性

-- 開啓分桶功能
set hive.enforce.bucketing=true;
-- 設置reduce個數爲-1,會自動根據桶個數決定reduce數
set mapreduce.job.reduces=-1;

插入數據

-- 因爲需要分區,所以要走mr任務的形式插入數據
-- 注意:load方法不走mr任務
-- 所以這裏使用select其他表的數據進行插入
insert into table studentbucket select * from student;
分桶表抽樣查詢

抽樣語法:TABLESAMPLE(BUCKET x OUT OF y)

注意:x的值必須小於等於y的值!

含義:x表示從哪個bucket開始抽取,

​ y表示總共抽取 (bucket數量 / y) 個bucket的數據,每隔一個y取下一個bucket

-- 抽樣查詢
-- 這裏是從bucket1開始抽取一個bucket數量(4/4=1)的數據
select * from studentbucket tablesample(bucket 1 out of 4 on id);
-- 這裏是從bucket1開始抽取2個bucket(第x=1和第x+y=3個bucket)的數據
select * from studentbucket tablesample(bucket 1 out of 2 on id);

數據導出

Insert 導出

-- 將查詢的結果導出到本地
insert overwrite local directory '/opt/datas' select * from student;
-- 將查詢的結果導出到本地,並按'\t'分割 
insert overwrite local directory '/opt/datas'
row format delimited fields terminated by '\t'
select * from student;
-- 將查詢的結果導出到HDFS
insert overwrite directory '/opt/datas' select * from student;

Hadoop 命令導出

# 直接將HDFS的文件直接發送到本地
hadoop fs -get /user/hive/warehouse/student /opt/datas

Hive Shell 命令導出

# 通過linux中的重定向符將查詢結果導出到文件
bin/hive -e "select * from student" > /opt/datas/student.txt;

Export 導出

-- 通過export導出至HDFS,並且保存了元數據
export table student to '/export/student';

數據刪除

Truncate 刪除

清空表中數據,只能刪除內部表,不能刪除外部表中的數據

-- 使用truncate清空表中數據
truncate table student;

函數

系統內置函數

-- 查看系統內置函數
show functions;
-- 查看系統內置函數的用法(split爲例)
desc function split;
-- 查看系統內置函數的詳細信息(split爲例)
desc function extended split;

自定義函數

UDF

User-Defined-Function

一進一出

如:split,datediff

繼承 org.apache.hadoop.ql.exec.UDF

實現 evaluate 方法

UDAF

User-Defined Aggregation Function

聚集函數,多進一出

類似:count/max/min

UDTF

User-Defined Table-Generating Functions

一進多出

如:lateral view explore()

繼承 org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;

實現三個方法 initialize,process,close

編程步驟

  1. 繼承org.apache.hadoop.ql.exec.UDF

  2. 需要實現 evaluate 函數;evaluate 函數支持重載;

  3. 在 hive 的命令行窗口創建函數

    • 添加 jar包

      add jar linux_jar_path
      
    • 創建 function

      create [temporary] function [dbname.]function_name AS class_name;
      
  4. 在 hive 的命令行窗口刪除函數

    Drop [temporary] function [if exists] [dbname.]function_name; 
    
  5. 注意事項

    UDF 必須要有返回類型,可以返回 null,但是返回類型不能爲 void;

Maven依賴

<dependencies> 
    <!--https://mvnrepository.com/artifact/org.apache.hive/hive-exec --> 
    <dependency> 
      <groupId>org.apache.hive</groupId> 
      <artifactId>hive-exec</artifactId> 
      <version>1.2.1</version> 
    </dependency> 
</dependencies> 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章