DDL

DDL 概述

数据定义语言:Data Definition Language,DDL,是 SQL 中对数据库内部对象(数据库 schema、数据表 table、数据视图 view、索引 index)的结构进行增删改的操作语言。

核心语法即:CREATEALTERDROP 等。

HQL 基本和 SQL 相同,在此基础上添加了一些特有的语法,例如分区 partition。

DDL 基础

Hive 数据类型

完整建表语法

CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name
[(col_name data_type [COMMENT col_comment], ……)]
[COMMENT table_comment]
[PARTITIONED BY (col_name data_type [COMMENT col_comment], ……)]
[CLUSTERED BY (col_name, col_name, ……) [SORTED BY (col_name [ASC|DESC], ……)] INTO num_buckets BUCKETS]
[ROW FORMAT DELIMITED [FIELDS TERMINATED BY char] [COLLECTION ITEMS TERMINATED BY char]  [MAP KEYS TERMINATED BY char] [LINES TERMINATED BY char] | SERDE serde_name WITH SERDEPROPERTIES (property_name=property_value, ……)]
[STORED AS file_format]
[LOCATION hdfs_path]
[TBLPROPERTIES (property_name=property_value, ……)]

大写字母为关键字。中括号内语法表示可选。分隔符表示左右语法二选一。


Hive 数据类型

Hive 中,数据类型分为:

  • 原生数据类型:数值、时间、字符串、杂项。

  • 复杂数据类型:array 数组、map 映射、struct 结构、union 联合体。

  • Hive 对英文字母大小写不敏感,除了 SQL 数据类型还支持 Java 数据类型(例如 String)。

  • 复杂类型的使用通常和分隔符语法配合。

  • 如果定义的数据类型不一致,Hive 会尝试隐式转换,但是不一定能成功

  • 除了隐式转换之外,还可以使用 CAST 来进行显式类型转换 CAST(数据字段 AS 新类型),例如 CAST ('INT' AS INT) 强制转为 INT 类型,但是转换失败,返回 NULL。

Hive 读写文件机制

SerDe

SerDe,是 Serializer、Deserializer 的简称目的是序列化(对象到字节码)和反序列化(字节码到对象)。Hive 可使用 SerDe 进行对象的读取和写入。

Hive 读写文件流程

Hive 读取文件:调用 InputFormat(Hadoop 默认是使用 TextInputFormat),返回 KV 键值对,然后调用 SerDe(默认是 LazySimpleSerDe),将 value 根据分隔符切分为各个字段。

Hive 写文件:调用 SerDe(默认是 LazySimpleSerDe)转为字节序列,然后调用 OutputFormat 写入 HDFS。

SerDe 相关语法

[ROW FORMAT DELIMITED | SERDE]

其中,ROW FORMAT 为关键字,DELIMITED 和 SERDE 二选一。

DELIMITED 表示使用默认的 SERDE 类:LazySimpleSerDe 类来进行数据处理。假如数据格式特殊,可以使用 ROW FORMAT SERDE serde_name 指定其他的 SERDE 类来进行处理,甚至还可以自定义 SERDE 类。

LazySimpleSerDe

LazySimpleSerDe 是 Hive 中默认的序列化类,可以指定字段之间、集合元素之间、map 映射之间、换行之间的分隔符,在建表的时候可以灵活搭配。

假如没有指定默认的 ROW FORMAT,那么默认的字段分隔符是 \001,使用的是 ASCII 编码,显示为 SOH,在 vim 中显示为 ^A。使用键盘无法打出。

Hive 数据存储

默认存储路径

Hive 默认存储在 ${HIVE_HOME}/conf/hive-site.xml 配置文件的 hive.metastore.warehouse.dir 中指定,默认值为 /user/hive/warehouse

Hive 的库、表均存储在此路径下。

指定存储路径

在 Hive 建表时可使用 LOCATION ${location} 指定在 HDFS 上的路径。

DDL 深入

Hive 内外表

Hive 内部表

内部表,Internal Table,被称为被 Hive 拥有和管理的托管表,默认创建的就是内部表。

当删除内部表时,Hive 会同时删除内部表的数据和内部表的元数据。

使用 DESCRIBE FORMATTED ${table_name} 查看表的描述信息,进而查看表的类型,其中 Table Type 为 MANAGED_TABLE 表示的就是内部表。

Hive 外部表

外部表,External Table,它的元数据被 Hive 管理但是实际数据不会被 Hive 管理。

简单来说,删除外部表不会删除表的数据,只会删除表的元数据。

外部表需要使用关键字 EXTERNAL 声明,并且一般搭配 LOCATION 指定数据的具体路径。

使用 DESCRIBE FORMATTED ${table_name} 查看表的描述信息,进而查看表的类型,其中 Table Type 为 EXTERNAL_TABLE 表示的就是外部表。

内外表的其他区别

除了数据管理的区别之外,它们还有其他区别:

内外表互相转换

-- 修改表为外部表
ALTER TABLE hero SET tblproperties ('EXTERNAL'='TRUE');
-- 查询表,此时 Table Type 一栏中已经变为了 EXTERNAL_TABLE,代表外部表
DESC FORMATTED hero;

-- 设置表为内部表,此时 Table Type 一栏中变为了 MANAGED_TABLE,代表内部表,也就是受管理的表
ALTER TABLE hero SET tblproperties ('EXTERNAL'='FALSE');

Hive 分区

分区概念

之前我们提到过分区这个概念,分区其实是为了帮助数据查找的一种手段。

比如,我们将一年的数据根据月份划分为十二个分区,在物理上,不同分区的数据会存储到 HDFS 中的不同文件夹下面。这样一来,查询某个月份的分区就只需要从某个文件夹下面查找,避免了全部数据的扫描。

分区需要使用 PARTITIONED BY 关键字指定按照什么字段进行分区:

CREATE TABLE hero(
    id int,
    name string
) PARTITIONED BY (role string)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY "\t";

上面这张表 hero 存在 id 和 name 两个字段,使用 role 字段进行分区,使用 DELIMITED 代表的 LazySimpleSerDe 进行文件读写,使用 “\t” 作为分隔符。

需要注意的是,分区字段 role 不能是表中已经存在的字段,因为最终分区字段也会以一种虚拟字段的形式显示在表结构上。

分区也分为静态分区和动态分区:

  • 静态分区:在创建表的时候手动指定分区字段和分区值。
  • 动态分区:基于查询结果动态推断出分区字段和分区值。

静态分区

之前已经说明了,静态分区是手动指定的分区字段和分区值,那么首先就要创建一个表:

CREATE TABLE hero (
      id int,
      name string,
      hp_max int,
      mp_max int,
      attack_max int,
      defense_max int,
      attack_range string,
      role_main string,
      role_assist string
) PARTITIONED BY (role string)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY "\t";

之后进行数据的导入,在导入时指定字段的静态分区,语法为:LOAD DATA [LOCAL] INPATH '' INTO TABLE ${table_name} PARTITION (${partition_key}=${partition_value})

LOAD 表示加载数据,之后详解。LOCAL 则表示从本地文件系统加载,默认是 HDFS 加载。

LOAD DATA LOCAL INPATH '/tmp/hero/archer.txt' INTO TABLE hero PARTITION (role='archer');
LOAD DATA LOCAL INPATH '/tmp/hero/assassin.txt' INTO TABLE hero PARTITION (role='assassin');
LOAD DATA LOCAL INPATH '/tmp/hero/mage.txt' INTO TABLE hero PARTITION (role='mage');
LOAD DATA LOCAL INPATH '/tmp/hero/support.txt' INTO TABLE hero PARTITION (role='support');
LOAD DATA LOCAL INPATH '/tmp/hero/tank.txt' INTO TABLE hero PARTITION (role='tank');
LOAD DATA LOCAL INPATH '/tmp/hero/warrior.txt' INTO TABLE hero PARTITION (role='warrior');

以上情况加载了文件,在 HDFS 上将会有分区,分别存储对应的文件:

动态分区

启用动态分区,首先要在 hive 的 session 中设置两个参数:

-- 开启动态分区
SET hive.exec.dynamic.partition=true;
-- 指定动态分区的模式为非严格模式,非严格模式下不必有静态分区;严格模式设置为 strict,要求至少有一个分区为静态分区。
SET hive.exec.dynamic.partition.mode=nonstrict;

执行动态分区,其实是执行的动态分区 + 插入,核心就是 INSERT + SELECT

-- 创建表,这里的内容和之前的静态分区的 hero 表相同
CREATE TABLE dynamicHero(
    id int,
    name string,
    hp_max int,
    mp_max int,
    attack_max int,
    defense_max int,
    attack_range string,
    role_main string,
    role_assist string
) PARTITIONED BY (role string)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY "\t";

-- 执行动态分区插入,核心思想是使用 INSERT + SELECT
-- 分区值将会通过返回字段的位置确定,普通的为 tmp.*,之后的 role 就是 tmp.role_main
INSERT INTO TABLE dynamicHero PARTITION(role)
SELECT tmp.id, tmp.name, tmp.hp_max, tmp.mp_max, tmp.attack_max, tmp.defense_max, tmp.attack_range, tmp.role_main, tmp.role_assist, tmp.role_main FROM hero tmp;

动态分区在插入的时候会使用位置自动推断。

之前讲过,分区字段 hero 在表现上也会占用一个字段,也就是说从我们的角度看,hero 表有四个字段:id、name、role_main、role。

以上这段 SQL 在查询的时候,首先会查询出原有的三个字段,分别对应着 id、name、role_main,之后我们查询出的 tmp.role_main 将会被分配到 hero 字段上,所以在分配分区的时候其实是根据 role_main 来分配的。

分区的本质

之前说过,分区的本质其实就是将分区分为了多个文件夹,数据存放到不同的分区文件夹中,分区文件夹的名字为 分区字段=分区值

分区字段是虚拟字段,数据不会存储在底层文件中。分区本质上是一种优化手段,是可选项。Hive 支持在分区的基础上继续分区,也就是多重分区。

多重分区

在多重分区下,分区是一种递进关系,在物理上表现为:在分区文件夹下面继续划分子分区文件夹。

创建多重分区只需要指定多个 partition,比如:CREATE TABLE hero(...) PARTITIONED BY (partition1 string, partition2 string, ...)

常见的多重分区,例如按照地区划分(省市县)、按照时间划分(年月日)等。

Hive 分桶

分桶概念

分桶表也叫做桶表,来源于 bucket。Hive 的分桶也是一种优化手段,不过和分区不同,分区是将数据分到多个文件夹下存储,但分区文件夹下面还是一份文件;分桶则是将一个分区中的数据分为多个文件。

桶的编号相同就会分到同一个 bucket 中。

分桶语法

[CLUSTERED BY (${col_name}) [SORTED BY (${col_name ASC|DESC})] INTO N BUCKETS];

CLUSTERED BY (${col_name}) SORTED BY (${col_name ASC|DESC}) 表示根据什么字段进行分,INTO N BUCKETS 表示分为 N 桶。

注意,和分区表不同,分桶表的字段必须是表中已经存在的字段。

-- 分桶,分桶表的字段必须为表中已经存在的字段
-- 按照 ID 划分为 5 桶
CREATE TABLE IF NOT EXISTS bucketHero(
    id int,
    name string,
    hp_max int,
    mp_max int,
    attack_max int,
    defense_max int,
    attack_range string,
    role_main string,
    role_assist string
) CLUSTERED BY (id) INTO 5 BUCKETS;

INSERT INTO bucketHero SELECT id, name, hp_max, mp_max, attack_max, defense_max, attack_range, role_main, role_assist FROM hero;

-- 按照 ID 划分为 5 桶,并且按照 attack_max 进行排序
CREATE TABLE IF NOT EXISTS bucketHeroWithSort (
    id int,
    name string,
    hp_max int,
    mp_max int,
    attack_max int,
    defense_max int,
    attack_range string,
    role_main string,
    role_assist string
) CLUSTERED BY (id) SORTED BY (attack_max DESC) into 5 BUCKETS;

INSERT INTO bucketHeroWithSort SELECT id, name, hp_max, mp_max, attack_max, defense_max, attack_range, role_main, role_assist FROM hero;

分桶表的好处

  1. 减少全表扫描。
  2. JOIN 时可以减少笛卡尔积。
  3. 数据量特别大时,可以使用分桶对数据进行抽样检测。

Hive 事务和视图

事务

Hive 的核心目标是为了数据的查询和分析用的,所以在设计之初不支持事务。

但是有些数据仍然有可能会出现一些错误,这些数据就需要修改和删除,那么从 Hive0.14 开始,Hive 就开始支持更新删除以及事务操作。

虽然 Hive 现在支持事务,但是 Hive 本质上不是用来做这个的,所以限制会比较多,并且不像 MySQL 那样方便:

  • 所有语言操作都是自动提交的,不支持手动事务。
  • 默认情况下为关闭,必须手动配置开启。
  • 必须是分桶表才可以支持事务功能。
  • 表的参数 transactional 必须为 true。
  • 外部表不可能成为 ACID 表。

视图

Hive 中的视图分为两种,一种是普通视图(view),一种是物化视图(materialized view)。

普通视图是用来简化操作的,比如我们有很复杂的查询语句,每次都写一遍太过复杂,就可以创建这样一种视图来简化操作,每次查询这个视图的时候就相当于执行了之前的复杂查询语句。

普通视图是虚表,不会提高查询性能,也没有缓存记录,当然也不可以进行数据的插入或者更改:

-- 创建视图
CREATE VIEW v_hero AS SELECT * FROM hero;

-- 从已有的视图中创建新的视图
CREATE VIEW v_hero_inside AS SELECT name FROM v_hero;

-- 展示视图
SHOW VIEWS;

-- 查看视图定义
SHOW CREATE TABLE v_hero_inside;

-- 删除视图,因为视图是虚拟的,所以不会对数据进行操作
DROP VIEW v_hero_inside;

-- 改变视图定义
ALTER VIEW v_hero AS SELECT attack_max FROM hero;

物化视图

物化视图和视图不同,这里的物化视图其实就可以看作是传统关系型数据库的物化视图。物化视图是一个包含查询结果的数据库对象,是实表,是有真实数据的,在查询的时候可以预先计算保存,然后避免耗时操作。

当然,因为通过了预计算得到了结果,当然需要一定的存储空间。Hive3.0 开始引入了物化视图,结果可以存储到 Hive 中,也可以存储到自定义的系统中。

Hive 引入物化视图就是为了抛弃 index 索引的语法,逐渐转为物化视图加快存储速度,但是当前特性还不是很多,只能说未来可期。

DDL 其余相关操作

数据库操作

数据库创建

CREATE SCHEMA | DATABASE [IF NOT EXISTS] demo
COMMENT "注释"
LOCATION "${HDFS LOCATION}"
WITH DBPROPERTIES (${属性名}=${属性值})
-- `COMMENT`:注释
-- `LOCATION`:指定存储到 HDFS 的位置,默认采用配置文件中的 `/user/hive/warehouse`
-- `WITH DBPROPERTIES`:指定一些数据库的属性配置。
CREATE SCHEMA IF NOT EXISTS demo
COMMENT "CREATE THE DEMO DATABASE"
WITH DBPROPERTIES ("author"="causes")

数据展示信息

-- 展示更多信息,DESCRIBE DATABASE/SCHEMA EXTENDED ${table_name};
-- EXTENDED 用于展示更多信息
DESCRIBE SCHEMA EXTENDED demo;

切换数据库

-- USE ${table_name}
USE demo;

删除数据库

-- 删除数据库,DROP DATABASE IF EXISTS ${demo} RESTRICT | CASCADE
-- RESTRICT:仅仅在数据库为空(没有表)才可以删除,默认值
-- CASCADE:强制删除
DROP DATABASE demo;

更改与当前数据库关联的元数据信息

-- ALTER DATABASE | SCHEMA ${db_name} SET DBPROPERTIES (${property_name}=${property_value})
ALTER DATABASE demo SET DBPROPERTIES ("author"="causes");

-- 更改用户所有者,ALTER DATABASE | SCHEMA ${db_name} SET OWNER USER | ROLE ${user};
ALTER SCHEMA demo SET OWNER ROLE causes;

-- 更改数据库位置,ALTER DATABASE | SCHEMA ${db_name} SET LOCATION ${HDFS_PATH}
ALTER DATABASE demo SET LOCATION "${HDFS_PATH}";

表操作

查看表

-- 查看表,DESCRIBE EXTENDED | FORMATTED ${table_name}
--  EXTENDED:Thrift 序列化形式展示表的元数据
-- FORMATTED:表格形式展示元数据
DESCRIBE FORMATTED hero;

删除表

-- 删除表,DROP TABLE IF EXISTS ${table_name} PURGE
-- 普通情况下,表数据不会被立刻删除,而是进入垃圾桶(假如已经配置了垃圾桶,则会进入 .Trash/Current 目录),假如指定了 PURGE 则会跳过垃圾桶,立刻删除
DROP TABLE IF EXISTS hero PURGE;

清空表

-- TRUNCATE TABLE ${table_name}
-- 清空表的数据,但是保留元数据,假如 HDFS 启动了垃圾桶则会进入垃圾桶,否则会被立刻删除
TRUNCATE TABLE hero;

修改表

-- 更改表名
ALTER TABLE table_name RENAME TO new_table_name;

-- 更改表属性
ALTER TABLE table_name SET TBLPROPERTIES (property_name = property_value, ... );

-- 更改表注释
ALTER TABLE student SET TBLPROPERTIES ('comment' = "new comment for student table");

-- 更改 SerDe 属性
ALTER TABLE table_name SET SERDE serde_class_name [WITH SERDEPROPERTIES (property_name = property_value, ... )];
ALTER TABLE table_name [PARTITION partition_spec] SET SERDEPROPERTIES serde_properties;
ALTER TABLE table_name SET SERDEPROPERTIES ('field.delim' = ',');

--移除 SerDe 属性
ALTER TABLE table_name [PARTITION partition_spec] UNSET SERDEPROPERTIES (property_name, ... );

-- 更改表的文件存储格式 该操作仅更改表元数据。现有数据的任何转换都必须在 Hive 之外进行
ALTER TABLE table_name  SET FILEFORMAT file_format;
-- 更改表的存储位置路径
ALTER TABLE table_name SET LOCATION "new location";

-- 更改列名称/类型/位置/注释
ALTER TABLE test_change CHANGE a a1 INT COMMENT 'this is column a1';;

-- 添加 / 替换列
-- 使用 ADD COLUMNS,您可以将新列添加到现有列的末尾但在分区列之前
-- REPLACE COLUMNS 将删除所有现有列,并添加新的列集
ALTER TABLE table_name ADD|REPLACE COLUMNS (col_name data_type,...);

Hive show 展示语法

SHOW 相关的语句提供了一系列的,查询 Hive Metastore 的命令,可以帮助用户查询相关信息,在这里我们讲解一下比较常见的内容。

-- 展示某个数据库中的所有表,不填 IN xxx 则为当前数据库。视图也是类似。
SHOW TABLES IN default;

-- 展示某张表的分区信息
SHOW PARTITIONS hero;

-- 展示表的扩展信息,例如名称、所属用户、位置、inputformat、outputformat 等
SHOW TABLE EXTENDED LIKE hero;

-- 展示表的属性信息,例如 是否为外部表、最后修改用户等
SHOW TBLPROPERTIES hero;

-- 展示表中的所有列,包括分区列
SHOW COLUMNS IN hero;

-- 展示可用函数,包括内置和自定义的
SHOW FUNCTIONS;

-- 查看表信息,可以使用 FORMATTED 格式化
DESC EXTENDED hero;

-- 查看数据库相关信息
DESCRIBE DATABASE default;

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。