HSQL

420 阅读8分钟

DDL(Data Definition Language)

数据库

创建

create database [if not exists] database_name
[comment database_comment] -- 数据库描述
[location  hdfs_path] -- hdfs中的位置
[with dbproperties (property_name=value, ...)]; -- 添加属性键值对 

查询

show databases [like 'identifier_with_wildcards']; -- like 模糊查询

修改

-- 修改dbproperties
alter database database_name set dbproperties (property_name=property_value, ...);

-- 修改location, 不会改变当前已有表的路径信息,而是改变后续创建表的默认父目录
alter database database_name set location hdfs_path;

-- 修改owner user
alter database database_name set owner user user_name;

删除数据库

drop database [if exists] database_name [restrict|cascade];

-- restrict 严格模式,数据库不为空删除失败,默认模式
-- cascade 级联模式,数据库不为空时,则将数据库中的表一并删除

切换数据库

use database_name;

创建表,普通

create [temporary] [external] table [if not exists] [db_name.]table_name
[(col_name data_type [comment col_comment], ...)]
[comment table_name]
[partitioned by (col_name data_type [comment col_comment], ...)]
[clustered by (col_name, col_name, ...)]
[sorted by (col_name [ASC|DESC], ...) into num_buckets nuckets]
[row format rowformat]
[stored as file_format]
[location hdfs_path]
[tableproperties (property_name=property_value, ...)]
  • 参数说明
1. temporary 
-- 临时表/内部表/管理表,该表只在当前会话可见,会话结束,表被删除,hive接管表的元数据以及hdfs中的数据

2. external 
-- 外部表,hive只接管元数据,不完全接管hdfs中的数据

3. partitioned by
-- 创建分区表, 将一张表的不同字段放入不同分区

4. clustred by ... sorted by ... into ... buckets 
-- 创建分桶表,将hive表数据分散存储到多个文件中clustred by 分桶, sorted by 桶内排序

5. row format
-- 指定sered, sered是Serializer and Deserializer的简写。Hive使用sered序列化和反序列化每行数据。
-- 语法一:
ROW FORAMT DELIMITED
[FIELDS TERMINATED BY char]
[COLLECTION ITEMS TERMINATED BY char]
[MAP KEYS TERMINATED BY char]
[LINES TERMINATED BY char]
[NULL DEFINED AS char]
-- fields terminated by 列分隔符
-- collection items terminated by    map、struct和array中每个元素之间的分割符
-- map keys terminated by      map中的key与value的分隔符
-- lines terminated by       行分隔符

-- 语法二:SERDE关键字可指定其他内置的SERDE或者用户自定义的SERDE。如JSON SERDE,可用于处理JSON字符串。 
ROW FORMAT SERDE serde_name [WITH 
SERDEPROPERTIES(property_name=property_value,property_name=property_value, ...)]

6. stored as
-- 指定文件格式,常用文件格式有 textfile(默认值), sequence file, orc file、parquet file等

7. location
-- 指定表所对应得我HDFS路径,若不存在路径,默认值为
-- ${hive.metastore.warehouse.dir}/db_name.db/table_name

8. tblproperties
-- 用于配置一些kv键值对参数
  • data_type 基本数据类型
Hive说明定义
tinyint1byte有符号整数 
smallint2byte有符号整数 
int4byte有符号整数 
bigint8byte有符号整数 
boolean布尔类型,true或者false 
float单精度浮点数 
double双精度浮点数 
decimal十进制精准数字类型decimal(16,2)
varchar字符序列,需指定最大长度,最大长度的范围是[1,65535]varchar(32)
string字符串,无需指定最大长度 
timestamp时间类型 
binary二进制数据
  • data_type 复杂数据类型
类型说明定义取值
array数组是一组相同类型的值的集合array< string >arr[0]
mapmap是一组相同类型的键-值对集合map< string, int >map['key']
struct结构体由多个属性组成,每个属性都有自己的属性名和数据类型struct<id:int, name:string>struct.id
  • 类型转换

创建表,CTAS建表

  • 该语法允许用户利用select查询语句返回的结果,直接建表,表的结构和查询语句的结构保持一致,且保证包含select查询语句返回的内容。(不能创建外部表)
CREATE TEMPORARY TABLE IF NOT EXISTS table_name
[COMMENT table_comment]
[ROW FORMAT row_format]
[STORED AS file_format]
[LOCATION hdfs_path]
[TBLPROPERTIES (property_name=property_value, ...)]
[AS select_statement]

create table like创建表

  • 该语法允许用户复刻一张已经存在的表结构,与上述的CTAS语法不同,该语法创建出来的表中不包含数据
CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name
[LIKE exist_table_name]
[ROW FORMAT row_format]
[STORED AS file_format]
[LOCATION hdfs_path]
[TBLPROPERTIES (property_name=property_value, ...)]

查看表

语法

-- 展示所有表
-- 注:like通配表达式说明:*表示任意个任意字符,|表示或的关系
show tables [in database_name] like ['*']

-- 查看表信息
describe [extended | formatted] [da_name.]table_name
-- 注:extended: 展示详细信息
--     formatted: 对详细信息进行格式化展示
-- 例:
desc formatted stu;

修改表

-- 重命名
alter table table_name rename to new_table_name

-- **修改列信息, 只修改表的元数据信息,不更改表在hdfs中的具体信息**
-- 1. 增加列,新增列位于吗,末尾
alter table table_name add columns (col_name data_type [comment col_comment], ...)

-- 2. 更新列,修改指定的列名、数据类型(需要考虑数据类型兼容)、注释信息以及在表中的位置
alter table table_name change [column] col_old_name col_new_name column_type [comment 
col_comment] [first|after column_name]
-- 若在修改表中的数据类型时,不让系统自动校验数据是否兼容,需要在修改前设置以下内容
set hive.metastore.disallow.incompatible.col.type.change=false;

-- 3.替换列,允许用户用新的列集替换表中的原有的全部列
alter table table_name replace columns (col_name data_type [comment col_comment], ...)

删除表(对外部表无效)

drop table [if exists] table_name;

清空表(对外部表无效)

truncate [table] table_name;

DML(Data Manipulation language)数据操作

Load(Load语句将文件导入Hive表中)

load data [local] inpath 'filepath' [overwrite] into table tablename [partition (partcoll=val1, 
partcol2=val2, ...)]

-- 关键字说明
local: 表示将文件从本地加载到Hive表中,否则从HDFS加载数据到HIVE表。此处的本地,若使用的是HiveClient
模式,则本地就是值HiveCli本地,若使用的是jdbc连接,则本地指的是hiveserver2的服务器。

overwrite: 表示覆盖表中已有数据,否则表示追加

partition: 表示上传文件到指定分区,若目标是分区表,需指定分区。

Insert

将查询结果写到表中

insert (into | overwrite) table tablename [partition] (partcol1=val1, patcol2=val2 ... )] 
select_statement;
-- 关键字说明
-- into : 将结果追加到目标表
-- overwrite : 用结果覆盖原有数据

给定values插入表中

insert into table student1 values(1, 'wangwu'), (2, 'zhaoliu');

将查询结果写入目标路径

insert overwrite [local] directory directory [row format row_format] [stored as file_format] 
select_statement

-- 案例
insert overwrite local directory '/opt/module/datas/student' ROW FORMAT SERDE 
'org.apache.hadoop.hive.serde2.JsonSerDe' 
select id,name from student;

Export&Import

  • Export导出语句可将表的数据和元数据信息一并到处的HDFS路径,Import可将Export导出的内容导入Hive,表的数据和元数据信息都会恢复。Export和Import可用于两个Hive实例之间的数据迁移。
-- 导出
export table tablename to 'export_target_path'

-- 导入
import [external] table new_or_original_tablename from 'source_path' [location 'import_target_path']

查询

语法

select [all | distinct] select_expr, select_expr, ...
from table_reference
[where where_condition] -- 过滤
[group by col_list] -- 分组查询, 使用分组查询时,查询内容只能包含分组字段以及聚合函数
[having col_list] -- 分组后过滤(接在group by后面)
[order by col_list] -- 排序
[cluister bby col_list | [distribute by col_list] [sort by col_list]]
[limit number] -- 限制输出的行数

join

select
    loc,
    count(*) cnt
from emp join dept
on emp.deptno = dept.deptno
group by loc;

内连接

  • innner join 默认连接方式, 只对两张表中共有的数据进行连接
select
    loc,
    count(*) cnt
from emp inner join dept
on emp.deptno = dept.deptno
group by loc;

左外连接

  • lfet join, 返回的数据包含左表的全部行,以及右表中能与左标关联的数据, 左表中与右表不关联的数据补null值。
select
    loc,
    count(*) cnt
from emp e
left join dept d
on e.deptno = d.deptno
group by loc;

右外连接

  • right join, 返回的数据包含右表的全部行,以及左表中能与右表关联的数据,右表中不与左表中关联的数据补null值。
select
    loc,
    count(*) cnt
from emp e
right join dept d
on e.deptno = d.deptno
group by loc;

满外连接

  • full join, 返回左右表中的全部行,其中不关联的数据补null值
select
    loc,
    count(*) cnt
from emp e
full join dept d
on e.deptno = d.deptno
group by loc;

笛卡尔积

  • 产生条件1:省略连接条件
  • 产生条件2:连接条件无效
  • 产生条件3:所有表中的所有行互相连接
  • 案例实操
select
    empno,
    dname
from emp, dept;

联合(union & union all)

  • union 和union all都是上下拼接sql的结果,这点和join有区别,join做的是左右连接,union和union all是上下拼接。union去重,union all不去重。
  • union和union all在上下拼接sql结果时有两个要求。
  • (1) 两个sql的结果,列的个数必须相同。
  • (2) 两个sql的结果,上下所对应列的类型必须一致。
  • 此外,union 只能连接两个select 语句,不能直接用于表连接,连接后的字段名以第一个select中的定义为基准。
select
    empno empid, ename, job, sal, deptno
from emp
where deptno = 40
union
select
    empno, ename, job, sal, deptno
from emp
where deptno = 40

-- 等价于

select
    *
from emp
where deptno in (20, 40);

排序

全局排序(Order by)

  • Order by 全局排序,只有一个reduce
  • asc (ascend) 升序,默认
  • desc (descend) 降序
  • oder by 会在map端执行,也会在reduce端执行,由于order by是全局排序,因此只能有一个reduce任务,当数据量大时,会造成reduce端数据倾斜,因此,常与limit联合使用
-- 排序第一字段为sal 降序,第二字段为 deptno 升序
select
    *
from emp
order by sal desc, deptno
limit 10;

-- limit 10的设置,在map端每个mattask会进行一次limit 10,减少输入到reduce的数据量,
-- 在reduce端会再执行一次 limit 10

每个Reduce内部排序(Sort by) Hive特有

  • Sort By:对于大规模的数据集order by的效率非常低。在很多情况下,并不需要全局排序,此时可以使用Sort by
  • Sort by为每个reduce产生一个排序文件。每个Reduce内部进行排序,对全局结果集来说不是排序。
set mapreduce.job.reduces = 3;

insert overwrite local directory '/usr/local/datas/empdata'
select
    *
from emp sort by deptno;

分区(Distribute By) Hive特有,用于tansform

  • Distribute By:在有些情况下,我们需要控制某个特定行应该到哪个Reducer,通常是为了进行后续的聚集操作。distribute by子句可以做这件事。distribute by类似MapReduce中partition(自定义分区),进行分区,结合sort by使用。
  • 对于distribute by进行测试,一定要分配多reduce进行处理,否则无法看到distribute by的效果。
set mapreduce.job.reduces = 3;

insert overwrite local directory '/usr/local/datas/empdata_distribute'
select
    *
from emp
distribute by job
sort by sal desc;

分区排序(Cluster By) Hive特有, 用于tansform

  • 根据某一个字段进行分区以及区内排序,等价于针对同一字段使用 sort by 以及distribute by

代码练习

多表联合

select
    t4.id id,
    coalesce(t1.score, 0) score1, -- coalesec(val1, val2, ...) 可判断多个值是否为null,取第一个不为null的值
    coalesce(t2.score, 0) score2, -- nvl(val1, val2) 可判断两个值是否为null,取第一个不为null的值
    coalesce(t3.score, 0) score3,
    t4.cnt cnt,
    t4.avgscore avgscore
from
(
    select
        stu_id id,
        score score
    from score_info where course_id=(
        select course_info.course_id from course_info where course_name='语文'
    )
) t1
full join (
    select
        stu_id id,
        score score
    from score_info where course_id=(
        select course_info.course_id from course_info where course_name='数学'
    )
) t2
on t1.id = t2.id
full join (
    select
        stu_id id,
        score score
    from score_info where course_id=(
        select course_info.course_id from course_info where course_name='英语'
    )
) t3
on nvl(t1.id, t2.id) = t3.id
join (
    select
        stu_id id,
        count(*) cnt,
        avg(score) avgscore
    from score_info
    group by stu_id
) t4
on coalesce(t1.id, t2.id, t3.id) = t4.id
order by t4.avgscore desc;

函数

  • 查看函数信息
-- 查看内置函数
show functions;

-- 查看内置函数用法
desc function upper;

-- 查看内置函数详细信息
desc function extended upper;

单行函数

  • 单行函数的特点是一进一出,即输入一行,输出一行。
  • 单行函数按照功能可分为如下几类:日期函数、字符串函数、集合函数、数学函数、流程控制函数等。

数值函数

-- 四舍五入, 保留两位小数
select round(3.3, 2)
select round(-1.5) -- -2

select ceil(0.5) -- 上取整
select floor(0.5) -- 下取整

字符串函数

  • 子串
-- substring, 与substr()用法一样
-- 返回值 string
select substring(str, pos[, len])
  • 替换
-- 将A中的子串b替换为c
-- 返回值 string
replace(string A, string B, string C)
select replace('atguigu', 'a', 'A')
  • 正则替换
-- 将字符串A中符合java正则表达式B的部分替换为C。,某些情况下要使用转义字符
-- 返回值string
re_gexp_replace(string A, string B, string C)
  • regexp 正则匹配
-- 字符串regex正则表达式
-- 返回值boolean
select  'dfsaaaaa' regexp 'dfsa+'
  • repeat 重复字符串
-- 将字符串重复n遍
select repeat('123', 3)
-- 输出:123123123
  • 字符串切割
-- 语法:split(string str, string par)
-- 返回值 string
-- pat可以是正则表达式
select split('a-b-c-d', '-')
  • nvl:替换null值
-- 语法:nvl(A, B)
-- 若A的值不为null, 则返回A, 否则返回B
select nvl(null, 1);
  • concat: 拼接字符串
-- 语法: concat(string A, string B, string C, ...)
-- 返回string
-- 说明:将A,B,C...等字符拼接为一个字符串
select concat('beijing', '-', 'sahnghai', '-', 'shenzhen')
  • concat_ws:以指定分割符拼接字符串或者字符串数组
-- concat_ws(string A, string ...| array(string))
-- 返回值:string
-- 描述: 使用分割符A拼接多个字符串,或者一个数组中的所有元素
select concat_ws('-', 'beijing', 'shanghai', 'shenzhen');
  • get_json_object: 解析json字符串
-- get_json_object(string json_string, string path)
-- 返回值:string
-- 说明:解析json的字符串json+_string, 返回path指定的内容。如果输入的json字符串无效,那么返回NULL。
select get_json_object('[{"name":"大海海","sex":"男","age":"25"},{"name":"小宋
宋","sex":"男","age":"47"}]','$.[0].name');
-- 输出 大海海

select get_json_object('[{"name":"大海海","sex":"男","age":"25"},{"name":"小宋
宋","sex":"男","age":"47"}]','$.[0]');
-- 输出:{"name":"大海海","sex":"男","age":"25"}

日期函数

  • unix_timestamp: 返回当前或指定时间的时间戳(UTC,格林威治时间)
-- 语法:unix_timestamp(), 不传参时默认当前的时间的时间戳(按照0时区时间进行准换)
-- 返回:bigint
select unix_timestamp('2022/08/08 08-08-08', 'yyyy/MM/dd HH-mm-ss');
  • from_unixtime: 转换UNIX时间戳(从1970-01-01 00:00:00 UTC到指定时间)到当前时区的时间格式
-- 语法:from_unixtime(bigint unixtime[, string format])
-- 返回值:string
select from_unixtime(1659946088);
-- 输出:2022-08-08 08:08:08
  • current_date: 当前日期
select current_date;
-- 输出:2022-07-11
  • current_timestamp:当前的日期加时间,并精确到毫秒
select current_timestamp;
-- 输出:2022-07-11 15:32:22.402
  • month: 获取日期中的日
-- 语法:month(string date)
-- 返回值 int
select month('2022-08-08 08:08:08')
-- 返回值 8
  • month_between()
-- 获取两个日期之间的月份
select months_between(current_date(), '2021-09-09')
  • day: 获取日期中的日
-- day(string date)
-- 返回值:int
select day('2022-08-08 08:08:08')
-- 输出 8
  • hour:获取日期中的小时
-- hour(string date)
select hour('2022-08-08 08:08:08') 
  • datediff: 两个日期相差的天数(结束日期减去开始日期的天数)
-- 语法:datediff(string enddate, string startdate)
-- 返回值:int
select datediff('2021-08-08','2022-10-09')
-- 输出:-427
  • date_add:日期加天数
-- 语法: date_add(string startdate, int days)
-- 返回值:string
select date_add('2022-08-08',2);
-- 输出:2022-08-10
  • date_sub:日期减天数
-- 语法:date_sub(string startdate, int days)
-- 返回值:string
select date_sub('2022-08-08',2);
-- 输出:2022-08-06
  • data_format: 将标准日期解析成指定格式字符串
select date_format('2022-08-08','yyyy年-MM月-dd日');

流程控制函数

  • case when
select
    stu_id,
    course_id,
    case
        when score >= 90 then 'A'
        when score >= 80 then 'B'
        when score >= 70 then 'C'
        when score >= 60 then 'D'
        else '不及格'
    end
from score_info;

-- 等值case when
select
    stu_id,
    course_id,
    case score
        when 90 then 'A'
        when 80 then 'B'
        when 70 then 'C'
        when 60 then 'D'
        else '不及格'
    end
from score_info
  • if 等价于问号表达式
select if(10 > 5,'正确','错误');
-- 输出 正确

集合函数(处理复杂数据类型)

  • size: 集合中元素的个数
-- 每一行函数中的friends集合里的个数
select size(friends) from test;
  • map:创建map集合
-- 语法:map(key1, value1, key2, value2, ...)
-- 说明:根据输入的key和value构建map类型
select map('xiaohaihai', 1, 'dahaihai', 2);
-- 输出:["xiaohaihai", "dahaihai"]
  • map_keys: 返回map中的key
select map_keys(map('xiaohai', 1, 'dahai', 2));
-- 输出:["xiaohai","dahai"]
  • map_values: 返回map中的value
select map_values(map('xiaohai', 1, 'dahai', 2));
-- 输出 [1, 2]
  • array声明array集合
-- 语法:声明array(val1, val2, …)
-- 说明: 根据输入的参数构建数组array类
select array('1','2','3','4');
-- 输出:["1","2","3","4"]
  • array_contains: 判断array中是否包含某个元素
select array_contains(array('a', 'b', 'c', 'd'), 'a');
  • sort_array: 将array中元素排序
select sort_array(array('a', 'd', 'c'));
  • struct声明struct中的各个属性
-- 语法:struct(val1, val2, val3, ...)
-- 说明: 根据输入的参数构建结构体struct类
select struct('name', 'age', 'weight');
-- 输出
{"col1":"name","col2":"age","col3":"weight"}
  • named_struct声明struct的属性和值
select named_struct('name', 'xiaosong', 'age', 18, 'weight', 80);
-- 输出
{"name":"xiaosong","age":18,"weight":80}

高级聚合函数

  • 多进一出(多行传入,一行输出)
  • collect_list 收集指定列元素并形成list集合,结果不去重
  • collect_set 收集指定列元素并形成set集合,结果去重
select
    collect_list(job),
    collect_set(job)
from employee;

炸裂函数(UDTF) User-Define-Table-Generating Functions

  • 接收一行函数,输出一行或者多行数据(制表函数)
  • explode(array< T > a)
- 输入一个数组,可将数据转换为多行元素返回(as item 是指定列名)
- 语法:select explode(array("a", "b", "c")) as item;
  • explode(Map<K, V> m)
- 输入一个map,返回多行两列,一列是key,一列是val (as (key, value) 用于指定列明,默认 key value)
- 语法:select explode(map("a", 1, "b", 2, "c", 3)) as (key, value);
  • posexplode(array< T > a)
- 输入一个数组,返回多行,两列,一列是索引,一列是数组值
- 语法:select posexplode(array("a", "b", "c")) as (pos, item);
  • inline(array< struct<f1:T1, ..., fn:Tn>> a)
- 输入一个结构体数组,返回多行多列,每行是一个结构体,每列是结构体的一个元素值
- 语法:select inline(array(
    named_struct("id", 1, "name", "zs"),
    named_struct("id", 2, "name", "ls"),
    named_struct("id", 3, "name", "ww")
)) as (id, name)
  • Lateral View(侧视图)
  • 定义:Lateral View 通常与UDTF配合使用。Lateral View可以将UDTF应用到源表中的每行数据,将每行数据转换为一行或多行,并将源表中每行的输出结果与该行关联起来,形成一个虚表。
-- tmp指UDTF炸出的表的别名,hobby是指表中每一个字段的别名,若有多个字段,需要用逗号分隔
select
    id,
    name,
    hobies,
    hobby
from person lateral view explode(hobbies) tmp as hobby;

窗口函数

概述

  • 窗口函数,能为每行数据划分一个窗口,然后对窗口范围内的数据进行计算,最后将计算结果返回给该行数据。
  • 窗口函数的语法中主要包括"窗口"和"函数"两部分。其中"窗口"用于定义计算范围,"函数"用于定义计算逻辑
  • 基本语法如下
select
    order_id,
    order_date,
    amount,
    函数(amount) over (窗口范围) total_amount
from order_info;

-- 窗口中内容为
partitinr by order by [column] range | rows between l and r

-- l的取值
unbounded preceding
[num] preceding

current row

[num] following

-- r的取值
[num] preceding
current row
[num] following
unbounded following

current row
[num] following
unbounded following

[num] following
unbounded following
  • 缺省
  • over()中的三部分内容 partition by 、ordered by 、 (rows | range) between ... and... 均可省略不写
-- 1. **partition by**省略不写,表示不分区
-- 2. **order by** 省略不写,表示不排序
-- 3. (rows|range) between...and... 省略不写,则使用其默认值,默认值如下:
--    若over()中包含order by,则默认值为: 
      range between unbounded preceding and current row
--    若over()中不包含order by, 则默认值为
      rows between unbounded preceding and unbounded following

常见窗口函数

聚合函数

跨行取值函数

  • lead 和 lag
-- 功能:获取当前行的 下 / 上 边某行、某个字段的值
-- 语法:
select
    order_id,
    user_id,
    order_date,
    amount,
    lag(order_date, 1, '1970-01-01') over (partition by user_id order_by order_date) last_date,
    lead(order_date, 1, '9999-12-31') over (partition by user_id order by order_date) next_date
from order_info;

-- lag(order_date, 1, '1970-01-01')
-- order_date: 字段名,1:偏移量,'1970-01-01':默认值
  • first value 和 last value
-- 功能:获取窗口内的某一列的第一个值和最后一个值
-- 语法:
select
    order_id,
    user_id,
    order_date,
    amount,
    first_value(order_date, false) over (partition by user_id order by order_date) first_date,
    last_value(order_date, false) over (partition by user_id order by order_date) last_date
from order_info;

-- first_value(order_date, false)
-- order_date: 字段名,获取该字段的第一个元素值,false: 是否跳过null值,是null值则读取下一行

排名函数

  • 不支持自定义窗口
select 
    stu_id,
    course,
    score,
    rank() over(partition by course order by score desc) rk,
    dense_rank() over(partition by course order by score desc) dense_rk,
    row_num() over(partition by course order by score desc) rn
from score_info;

-- rank() 在处理相同值时会产生以下排名: 1 1 3
-- dense_rank() 在处理相同值时会产生以下排名: 1 1 2
-- row_num() 在处理相同值时会产生以下排名: 1 2 3

几种去重方法(面试题)

-- 1. 通过distinct去重
select
    distinct
    id,
    name
from table;

-- 2. 通过group by去重
select
    id, name
from table 
group by id, name;

-- 3. 通过窗口函数去重
-- 通过id, name进行分区,并给每个分区的数据分配编号,编号大于1一定是重复数据
select
    id, name
from (
    select
        id, name row_number() over (partition by id, name) rn
    from table
) t1
where t1.rn = 1;