在Mysql中没有表类型这个概念,因为它就只有一种表。
但是Hive中是有多种表类型的,我们可以分为四种,内部表外部表分区表桶表

内部表

内部表是hive默认表类型,也叫管理表(MANAGED TABLE),表数据默认存储在/user/hive/warehouse目录中
在加载数据的过程中,实际数据会被复制到/user/hive/warehouse目录中
删除表时,表的数据和元数据将会被同时删除

外部表(external ★)

建表语句中包含external 的表叫外部表
外部表在加载数据的时候,实际数据并不会移动到warehouse目录中,只是与外部数据建立一个链接(映射关系),数据只是表对hdfs上的某一个目录的引用而已
当删除一个外部表时,数据依然是存在的。仅删除表和数据之间的引用关系,所以这种表是安全的,就算是误删表,数据还是存在的

示例

  • 创建外部表external_table

    1. create external table external_table
    2. (
    3. key string
    4. ) location '/data/external';
  • 查看HDFS

image.png

  • 加载数据

image.png

load data local inpath '/data/soft/hivedata/external_table.data' into table external_table;
  • 查看external_table表和HDFS

image.png
image.png

  • 删除表,可以发现文件还在

    hive> drop table external_table;
    OK
    Time taken: 0.221 seconds
    hive> show tables;
    OK
    stu
    stu2
    stu3
    student
    t1
    t2
    t3
    t3_new
    Time taken: 0.087 seconds, Fetched: 8 row(s)
    

    image.png

  • 再次创建表,可以再关联上

    内部表和外部表的相互转化

    ```sql — 内部表转外部表 alter table tblName set tblproperties (‘external’=’true’);

— 外部表转内部表 alter table tblName set tblproperties (‘external’=’false’);

<a name="zZQ6j"></a>
## 内部表和外部表的对比
前面使用内部表的过程:将一个文件加载到warehouse目录中,两份的内容其实是一样的。<br />使用外部表就不用复制一份一样的数据。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/5363330/1642489209959-313ced2f-95bb-436c-a892-890a6db2ad94.png#clientId=u9506534f-aa9f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=63&id=uca65023d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=63&originWidth=445&originalType=binary&ratio=1&rotation=0&showTitle=false&size=6841&status=done&style=none&taskId=ua933009a-6f94-4af0-b567-117e7045b56&title=&width=445)![image.png](https://cdn.nlark.com/yuque/0/2022/png/5363330/1642502745065-e18ee517-78a9-498c-a80b-5687320b212c.png#clientId=u988f852f-312f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=432&id=ua6c5977f&margin=%5Bobject%20Object%5D&name=image.png&originHeight=432&originWidth=1070&originalType=binary&ratio=1&rotation=0&showTitle=false&size=38941&status=done&style=none&taskId=u6bdc5b94-e9c9-4914-86ec-3d9dc24802d&title=&width=1070)<br />在实际工作中,我们在 hive 中创建的表 95% 以上的都是外部表,先通过 flume 采集数据,把数据上传到 hdfs 中,然后在 hive 中创建外部表和 hdfs 上的数据绑定关系,就可以使用 sql 查询数据了,所以连 load 数据那一步都可以省略了,因为是先有数据,才创建的表<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/5363330/1642926576113-cc643cc4-d0a6-4e39-9e2d-7bb5f96ce56e.png#clientId=u3baed50b-df1a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=189&id=uf2583c41&margin=%5Bobject%20Object%5D&name=image.png&originHeight=189&originWidth=785&originalType=binary&ratio=1&rotation=0&showTitle=false&size=31672&status=done&style=none&taskId=u1c06540e-4e4c-486f-a445-c6b7d7849ff&title=&width=785)
<a name="yns3x"></a>
# 分区表(partitioned by)
> 假设web服务器每天产生一个日志文件,flume会把日志文件采集到HDFS上,每天的数据存储在一个目录里
> 格式类似:
> hdfs:
> /data/logs/20200101
> /data/logs/20200102
> /data/logs/20200103

分区可以理解为分类,通过分区把不同类型的数据放到不同目录中,<br />分区的标准就是指定分区字段,分区字段可以有一个或多个。<br />分区的意义在于优化查询,查询时尽量利用分区字段,如果不使用分区字段,就会全表扫描,最典型的一个场景就是把天作为分区字段,查询的时候指定天。

<a name="JHews"></a>
## 内部分区表
<a name="Q8qGH"></a>
### 示例一:一个分区字段
数据<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/5363330/1642561912840-a3220ec7-2f42-4723-98f8-0c757d2f9a4e.png#clientId=ud4d9c70f-f555-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=62&id=ua137ddc2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=62&originWidth=382&originalType=binary&ratio=1&rotation=0&showTitle=false&size=3804&status=done&style=none&taskId=u24ab3978-e19d-453c-b380-a951d99bf6c&title=&width=382)

- 创建表
```sql
create table partition_1
(
    id   int,
    name string
) partitioned by (dt string)
    row format delimited
    fields terminated by '\t';
  • 查看表信息

image.png

  • 加载数据并指定分区

    load data local inpath '/data/soft/hivedata/partition_1.data' into table partition_1 partition(dt='20200101');
    

    image.png image.png

  • 创建分区,不加数据:

    alter table partition_1 add partition(dt='20200102');
    

    image.png
    此时,20200102分区可以通过load或者hdfs dfs -put加载数据

  • 查看partition_1表的分区

    show partitions partition_1;
    

    image.png

  • 删除表的分区

    alter table partition_1 drop partition(dt='20200102');
    

    image.png

    示例二:多个分区字段

  • 数据

image.png

  • 创建表

    create table partition_2
    (
      id   int,
      name string
    ) partitioned by (year int,school string)
      row format delimited
      fields terminated by '\t';
    
  • 查看表信息

image.png

  • 加载数据并指定分区 ```sql load data local inpath ‘/data/soft/hivedata/partition_2.data’ into table partition_2 partition(year=2020,school=’jsz’);

load data local inpath ‘/data/soft/hivedata/partition_2.data’ into table partition_2 partition(year=2020,school=’english’);

load data local inpath ‘/data/soft/hivedata/partition_2.data’ into table partition_2 partition(year=2019,school=’jsz’);

load data local inpath ‘/data/soft/hivedata/partition_2.data’ into table partition_2 partition(year=2019,school=’english’);


- 查看partition_2表

![image.png](https://cdn.nlark.com/yuque/0/2022/png/5363330/1642573087070-6057608e-29d4-4ce8-8dbe-ccced4736091.png#clientId=ud4d9c70f-f555-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=413&id=u76fea1ca&margin=%5Bobject%20Object%5D&name=image.png&originHeight=413&originWidth=513&originalType=binary&ratio=1&rotation=0&showTitle=false&size=47085&status=done&style=none&taskId=u1e4a28e1-0cff-406c-b74f-277882d22f8&title=&width=513)

- 查看'zhangsan'的学生信息
```sql
select * from partition_2 where name='zhangsan';

image.png

  • 查看2019级,计算机院的学生

    select * from partition_2 where year=2019 and school='jsz';
    

    image.png

  • 查看HDFS

image.png

  • 查看partition_2表的分区

    show partitions partition_2;
    

    image.png

    外部分区表(★)

    示例

  • 数据

image.png

  • 建外部分区表

    create external table ex_par
    (
      id   int,
      name string
    ) partitioned by (dt string)
      row format delimited
      fields terminated by '\t'
      location '/data/ex_par';
    
  • 加载数据

    load data local inpath '/data/soft/hivedata/ex_par.data' into table ex_par partition(dt='20200101');
    
  • 查看ex_par表和HDFS

image.png image.png

  • 删除分区20200101

    alter table ex_par drop partition(dt='20200101');
    
  • 查看数据和分区:都查不到,但是HDFS数据还在(因为是外部表)

image.png image.png

  • 再次添加分区并绑定
    alter table ex_par add partition(dt='20200101') location '/data/ex_par/dt=20200101';
    
    image.png

    总结

    image.png
    load data local inpath '/data/soft/hivedata/ex_par.data' into table ex_par partition(dt='20200101');
    
    这条命令做了两件事:1、上传数据 2、指定分区
    等价于: ```shell

    HDFS创建目录/data/ex_par/dt=20200101

    hdfs dfs -mkdir /data/ex_par/dt=20200101

HDFS上传文件ex_par.data到/data/ex_par/dt=20200101目录

hdfs dfs -put /data/soft/hivedata/ex_par.data /data/ex_par/dt=20200101

绑定分区

alter table ex_par add partition(dt=’20200101’) location ‘/data/ex_par/dt=20200101’;

> 实际工作中:
> 先通过 flume 采集数据,把数据上传到 hdfs 中。不需要再load,只要每天定时加分区:`alter table ex_par add partition(dt='20200101') location '/data/ex_par/dt=20200101';`

<a name="xwfcc"></a>
# 桶表(防数据倾斜)
![image.png](https://cdn.nlark.com/yuque/0/2022/png/5363330/1642582814476-a24d08df-d352-4ed9-b8d0-8c7791699df7.png#clientId=u1e0fb0ce-588a-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u9b1cdc2c&margin=%5Bobject%20Object%5D&name=image.png&originHeight=198&originWidth=562&originalType=url&ratio=1&rotation=0&showTitle=false&size=18773&status=done&style=none&taskId=u194d9f11-bd3f-4f4a-a141-3c90a8920b4&title=)<br />桶表是对数据进行哈希取值,然后放到不同文件中存储。物理上每个桶就是表(或分区)里的一个文件。
> 当数据不均衡的时候会用到桶表,比如,针对中国人口主要集中在河南,江苏,山东,广东,四川等地,西藏就三四百万比较少,如果使用分区表,把省份作为分区字段,数据会集中在某几个分区,这样计算的时候会出现数据倾斜的问题,效率会变得较低,这样从源头上解决,就可以采用分桶的概念,相对均匀的存放数据,也就是使用分桶表。

<a name="gNKzc"></a>
## 示例

- 创建桶表
> 这个表的意思是按照id进行分桶,分成4个桶。

```sql
create table bucket_tb
(
    id int
) clustered by (id) into 4 buckets;

桶表不能用load data的方式加载数据,而是需要使用其它表中的数据,类似这样的写法:insert into table … select … from …
在插入数据之前需要先设置开启桶操作,不然数据无法分到不同的桶里面,set hive.enforce.bucketing=true;
其实这里的分桶就是设置reduce任务的数量,因为分了多少桶,最终结果就会产生多少个文件,最终结果中文件的数量就和reduce任务的数量是挂钩的,设置完 set hive.enforce.bucketing=true; 可以自动控制reduce的数量从而适配bucket的个数。

  • 初始化一个表,用于向桶表中加载数据
    原始数据文件是这样的

image.png

create table b_source(id int);

load data local inpath '/data/soft/hivedata/b_source.data' into table b_source;

image.png

  • 向桶表中加载数据

    hive> set hive.enforce.bucketing=true;
    hive> insert into table bucket_tb select id from b_source;
    
  • 查看分桶后的bucket_tb

image.png

  • 查看HDFS:按照我们设置的桶的数量为4,这样在hdfs中会存在4个对应的文件,每个文件的大小是相似的

image.png

桶表的作用

1、数据抽样
假如我们使用的是一个大规模的数据集,我们只想去抽取部分数据进行查看.使用bucket表可以变得更加的高效

select * from bucket_tb tablesample(bucket 1 out of 4 on id);

-- tablesample是抽样语句
-- 语法解析:tablesample(bucket x out of y on column)
-- y尽可能是桶表的bucket数的倍数或者因子,而且y必须大于等于x
-- y表示把桶表中的数据随机分为多少桶
-- x表示取出第几桶的数据

image.png

bucket 1 out of 4 on id:根据id对桶表中的数据重新分桶,分成4桶,取出第1桶的数据
bucket 2 out of 4 on id:根据id对桶表中的数据重新分桶,分成4桶,取出第2桶的数据
bucket 3 out of 4 on id:根据id对桶表中的数据重新分桶,分成4桶,取出第3桶的数据
bucket 4 out of 4 on id:根据id对桶表中的数据重新分桶,分成4桶,取出第4桶的数据

2、提高某些查询效率
例如:join 查询,可以避免产生笛卡尔积的操作

select a.id, a.name, b.addr from a join b on a.id=b.id;

如果a表和b表已经是分桶表,而且分桶的字段是id,那么这个操作的时候就不需要再进行全表笛卡尔积了,因为分桶之后相同的规则的id已经在相同的文件里面了,这样a表的每个桶就可以和b表的每个桶直接join,而不用全表join了

视图

hive中,也有视图的概念,视图实际上是一张虚拟表,是对数据的逻辑表示,它的主要作用是为了降低查询的复杂度

使用create view命令来创建一个视图

create view v1 as select  t3_new.id,t3_new.stu_name from t3_new;

此时通过show tables也可以查看到这个视图
image.png
查看视图的结构,显示的内容和表显示的内容是没有区别的
image.png
通过视图查询数据
image.png

注意:视图在/user/hive/warehouse中是不存在的。因为它只是一个虚拟的表。