# 七、Hive 中的 DDL 和 DML

在前几章中，我们已经查看了一些 HiveQL 查询，我们可以看到它们主要是 SQL，包括一些特定于 Hive 的语句和子句。HiveQL 并不完全与 ANSI-SQL 兼容(尽管实现 SQL-92 的兼容性是未来版本的目标)，但差异是在边缘发现的——任何有 SQL 经验的人都可以轻松地使用 HiveQL。

与 SQL 一样，HiveQL 语句或者用数据定义语言(DDL)定义数据库的结构，或者用数据修改语言(DML)更改数据查询的内容，或者读取数据。

Hive 只提供表、视图和索引对象，这意味着只有有限数量的 DDL 语句，并且因为它的主要功能是作为数据仓库，所以标准的 SQL DML 语句并不是在所有情况下都受支持。

在本章中，我们将介绍定义数据结构和写入数据的 HiveQL 的关键部分。我们还将涵盖所有主要语句，但是 Hive Wiki 上的[语言手册为所有语句提供了出色的文档，包括支持它们的 Hive 版本。](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL)

DDL 语句用于定义或更改 Hive 数据库和数据库对象。HiveQL 的功能随着每个版本而发展，这意味着不是所有的语句，也不是所有的子句，在所有版本中都可用。在本章中，我们将介绍在编写本文时的当前版本(1.2.1)中最常用的 DDL 语句。

Hive 中的所有对象都存在于一个模式中，但是您不需要指定特定的模式，事实上通常使用默认模式。如果想要隔离数据，可以创建不同的模式，并通过在模式名称前加前缀来引用对象。

术语数据库和模式在 Hive 中是可以互换的，因此以下语句可以用于模式或数据库，并且以相同的方式工作:

*   CREATE SCHEMA[name]—创建新架构。
*   使用[名称]—切换到命名模式。

可以使用 with dbproperties 子句创建(或更改)模式，以便将键值对的集合存储为关于模式的元数据。这对于存储数据库版本或模式的任何其他信息值非常有用，您可以使用扩展的 description 语句来查看这些信息值，如代码清单 62 所示。

 62:模式属性

```
  > create database iot with
  dbproperties('maintainer'='elton@sixeyed.com', 'release'='2016R1');
  No rows affected (0.249 seconds)
  > describe schema extended iot;
  +----------+----------+---------------------------------------------------
  | db_name  | comment  |                    
  location                      | owner_name  | owner_type  |                
  parameters                 |
  +----------+----------+---------------------------------------------------
  | iot      |          |
  hdfs://localhost:9000/user/hive/warehouse/iot.db  | root        | USER       
  | {maintainer=elton@sixeyed.com, release=2016R1}  |
  +----------+----------+---------------------------------------------------

```

Hive 中唯一的数据库对象是表、视图和索引，这意味着唯一的 create 语句是我们在第 1 章介绍 Hive 时已经使用过的语句:

*   创建表—创建内部配置单元表。
*   创建外部表—创建一个将数据存储在配置单元外部的表。
*   创建视图—在一个或多个表格上创建视图。
*   创建索引—在现有表(内部或外部)上创建索引。

因为所有这些语句都支持 if not exists 子句，所以 Hive 只会在数据库中不存在它们的情况下创建它们。

创建指定列名和数据类型、存储引擎和数据位置的表。内部表也可以分区以提高可伸缩性，我们将在第 8 章“数据分区”中详细介绍。

可以为任何返回值的 HiveQL 查询创建视图，这意味着您可以创建视图，以便根据需要使用联接或联合构造来提供对复杂数据组合的简单访问。

索引是为单个表中的一列或多列创建的。

| ![](img/00009.jpeg) | 提示:定义对象仅意味着 Hive 将定义保存在其元数据存储中。当查询对象时使用该存储，但是在创建时，只有对 DDL 的基本验证——Hive 确保 HiveQL 有效，并且数据源存在，但是没有验证任何列映射。 |

create 语句大体上是标准的 SQL，但是 Hive 提供了一个节省时间的选项来创建与现有表具有相同结构的表，创建类似表的表，如代码清单 63 所示。

 63:像创建其他表一样创建表

```
  > describe dual;
  +-----------+------------+----------+--+
  | col_name  | data_type  | comment  |
  +-----------+------------+----------+--+
  | c         | string     |          |
  +-----------+------------+----------+--+
  > create table dual2 like dual;
  > describe dual2;
  +-----------+------------+----------+--+
  | col_name  | data_type  | comment  |
  +-----------+------------+----------+--+
  | c         | string     |          |
  +-----------+------------+----------+--+

```

create table like 语句对于处理临时表中的数据或在结构相同的内部表和外部表之间移动数据通常很有用。它不复制任何数据，也不链接表，这意味着改变原来的表结构不会影响新的表结构。

现有对象可以用 alter 语句进行更改，但通常这些语句只会影响 Hive 元数据存储中的对象结构，不会更改任何现有数据。

因此，在更改表定义时，您必须小心，因为您可以很容易地修改您的表，并使其无法读取。使用 alter table，您可以重命名表，更改文件格式，以及添加、删除或更改列。

要更改现有列，语法是 alter table … change，它允许您更改列名、数据类型和表中的顺序。代码清单 64 显示了 syslogs 表中的一个现有列从时间戳更改为字符串类型，并移动到表中稍后的位置。

 64:更改表格以移动列

```
  > describe syslogs;
  +-----------+------------+----------+--+
  | col_name  | data_type  | comment  |
  +-----------+------------+----------+--+
  | loggedat  | timestamp  |          |
  | host      | string     |          |
  | process   | string     |          |
  | pid       | int        |          |
  | message   | string     |          |
  +-----------+------------+----------+--+
  > alter table syslogs change loggedat string after host;
  > describe syslogs;
  +-----------+------------+----------+--+
  | col_name  | data_type  | comment  |
  +-----------+------------+----------+--+
  | host      | string     |          |
  | loggedat  | string     |          |
  | process   | string     |          |
  | pid       | int        |          |
  | message   | string     |          |
  +-----------+------------+----------+--+

```

我们可以从第二个 description 语句中看到，更改成功，但是 Hive 没有更改表中的数据。数据文件保持原始结构，因此第一列是时间戳(原始日志数据列)，第二列是字符串(原始主机列)。

如果我们尝试查询该表，Hive 会读取第一列中的时间戳值，该值包含 loggedAt 时间戳，但会尝试将其作为字符串读取。格式不兼容，所以我们得到一个错误，如代码清单 65 所示。

 65:结构不匹配的表

```
  > select * from syslogs limit 5;
  Error: java.io.IOException:
  org.apache.hadoop.hive.ql.metadata.HiveException:
  java.lang.ClassCastException:
  org.apache.hadoop.hive.serde2.io.TimestampWritable cannot be cast to
  org.apache.hadoop.io.Text (state=,code=0)

```

我们仍然可以访问表中的其他列，但是现在数据放在了错误的位置——元数据显示 loggedAt 列在第二个位置，但是在数据文件中，该位置包含主机字段，如代码清单 66 所示。

 66:获取错误数据

```
  0: jdcbc:hive2://127.0.0.1:10000> select loggedat, process,
  pid, message from syslogs limit 1;
  +------------+----------+------+------------------------------+--+
  |  loggedat  | process  | pid  |          
  message            |
  +------------+----------+------+------------------------------+--+
  | sc-ub-xps  | anacron  | 804  | Job
  `cron.daily' terminated  |
  +------------+----------+------+------------------------------+--+

```

因为 alter table 在元数据级别工作，所以很容易通过将表更改回其原始定义来修复损坏。如果您确实需要更改表的现有结构(除了添加或重命名列)，更好的方法是定义一个新表，并使用您需要的任何转换从现有表加载它。

使用 alter view，您可以更改投影视图的 select 语句。您可以通过更改查询来更改列布局、数据类型和顺序，并且，如果 HiveQL 查询有效，视图也将有效。视图没有在 Hive 中具体化，这意味着视图后面没有数据文件会与定义不同步。

如果视图尚不存在，alter view 将引发错误。代码清单 67 更改了 HBase 设备事件表的现有视图，从而删除了原始的 rowkey 列并添加了一个子句，以便只返回在 period 列中有值的行。

 67:更改视图

```
  > alter view device_events_period as select split(rowkey,
  '\\|')[0] deviceid, split(ROWKEY, '\\|')[1] period, eventname, receivedat from
  device_events where split(ROWKEY, '\\|')[1] is not null;

```

alter index 语句只允许您重建现有索引，您不能更改索引使用的列或表。Hive 不会自动重建索引，这意味着每当您修改基础表中的数据时，都需要使用 alter index 手动重建。

代码清单 68 在外部 hbase_table 上重建索引(具体化为内部 Hive 表)。请注意，必须显式指定索引的表。

 68:重建索引

```
  > alter index ix_hbase_table_cf1_data on hbase_table rebuild;
  ...
  No rows affected (51.917 seconds)

```

您可以使用 drop table、 **drop view** 和 drop index 语句从 Hive 中移除对象，同时可以选择使用 if exists 子句。

当您删除一个索引时，Hive 会同时删除该索引和用于存储它的内部表。除非在任何查询中显式引用了内部索引表，否则删除索引不会对功能产生影响。否则，任何隐式使用索引的查询都将运行得更慢，但它们仍然会产生相同的结果。

当您删除一个视图时，它会从数据库中删除，使用它的任何查询都会失败，并从 Hive 中返回一个“找不到表”的错误，如代码清单 69 所示。

 69:删除视图

```
  > drop view device_events_period;
  > select * from device_events_period;
  Error: Error while compiling statement: FAILED: SemanticException
  [Error 10001]: Line 1:14 Table not found 'device_events_period'
  (state=42S02,code=10001)

```

因为 Hive 中没有实体化视图，所以删除视图不会删除任何数据。

删除表时，对数据的影响因所用表的类型而异。对于外部表，Hive 不会删除源数据，因此，如果删除基于 HDFS 文件或 HBase 表的表，基础数据将会保留，尽管您无法通过 Hive 表访问它。

代码清单 70 显示了删除 Hive 中的外部设备事件表和扫描 HBase 中的底层设备事件表的结果。

 70:删除外部表

```
  > drop table device_events;
  > select * from device_events;
  Error: Error while compiling statement: FAILED: SemanticException
  [Error 10001]: Line 1:14 Table not found 'device_events'
  (state=42S02,code=10001)
  …
  hbase(main):004:0> scan 'device-events'
  ROW                                COLUMN+CELL                                                                                     

   uuid2                             column=e:n,
  timestamp=1454520396475, value=power.off                                             

```

使用内部表，Hive 管理存储，这样当您删除它们时，所有数据都将被删除。在某些平台设置中，底层文件可能会被移动到可恢复的废纸篓文件夹中，但不要依赖它。

为了确保永久删除，请指定 purge 子句，如代码清单 71 所示，其中显示了删除 syslog_sumaries 表前后的 HDFS 文件列表。

 71:删除内部表

```
  root@hive:/hive-setup# hdfs dfs -ls /user/hive/warehouse/syslog_summaries
  Found 1 items
  -rwxrwxr-x   1 root supergroup        423 2016-02-02 21:21
  /user/hive/warehouse/syslog_summaries/000000_0
  ..
  > drop table syslog_summaries purge;
  …
  root@hive:/hive-setup# hdfs dfs -ls
  /user/hive/warehouse/syslog_summaries
  ls: `/user/hive/warehouse/syslog_summaries': No such file or
  directory

```

当您删除一个表时，Hive 不会警告依赖关系，它会让您删除具有基于依赖关系的视图或索引的表。当您删除视图引用的表时，视图将保留，但如果您尝试查询它们，则会出错。删除索引表时，索引和基础索引表将被无声地删除。如果您想从一个内部 Hive 表中删除数据，但保留该表，那么标准的 SQL truncate 语句会删除所有的行，如代码清单 72 所示。

代码清单 72:截断数据

```
  > select count(*) from syslogs;
  +--------+--+
  |  _c0   |
  +--------+--+
  | 15695  |
  +--------+--+
  > truncate table syslogs;
  No rows affected (0.09 seconds)
  > select count(*) from syslogs;
  +------+--+
  | _c0  |
  +------+--+
  | 0    |
  +------+--+

```

truncate 语句的工作原理是删除 HDFS 的基础文件，同时保留文件夹结构，以便可以再次填充该表。因为它仅对内部表有效，所以如果您尝试截断外部表，Hive 将引发错误“无法截断非托管表”。

DML 语句的全部范围是 Hive 的最新增加，并非所有存储引擎都支持。从数据仓库的起源来看，Hive 最初并不是为了更新或删除现有数据而设计的；它只支持用 load 和 import 语句追加数据。

自 Hive 0.14 以来，为支持它们的存储引擎提供了更新和删除语句。insert 语句也进行了扩展，以便允许直接插入值，而在以前的版本中，我们只能插入选择查询的结果。

完整的 DML 语句集仅在支持典型关系数据库管理系统设计的 ACID 属性的表上可用。ACID 原则确保数据一致性，因此对同一表的同时读取和写入不会导致冲突。

ACID 原则在 HDFS 并不是固有的，它不允许文件中的数据被更改，也不锁定正在附加的文件。这意味着您不能更新文件中现有的数据项，也不能阻止读者在写入新数据时访问它。

Hive 通过创建增量文件来解决 HDFS 的局限性——Hive Transactions 上的维基页面解释了其中的复杂性。目前，只有内部表支持 Hive 事务，并且只有当它们以正确的文件格式存储并且 Hive 被配置为支持事务管理器时。

从 Hive 1.2.1 开始，为了支持 ACID，表必须具有以下属性:

*   内部表。
*   ORC 格式存储。
*   分时段但不排序(更多信息见第 8 章数据分区)。
*   用事务属性标记。

因为 Hive 的对象数量比 SQL 数据库少，所以只有很少的 DDL 语句，表定义的多样性意味着有大量的 Hive 特定子句。在这一章中，我们已经讨论了创建、修改和删除对象。

记住存储在 Hive metastore 中的对象结构和文件中数据的实际结构之间的脱节，是关于 DDL 的关键要点。因为 Hive 通常不强制执行结构，所以如果您更改表，结构和内容将会不同步，数据将无法访问。

从最初的追加或覆盖数据仓库，Hive 已经发展到支持大多数 SQL DML 语句，尽管只支持有限的表类型子集。对 ACID 表类型和事务的支持是有用的，但是请记住，Hive 并不打算作为事务数据库。如果您发现您的实现受到 Hive 中 DML 支持的限制，您可能没有正确使用 Hive。

在下一章中，我们将研究 Hive 表设计中提升性能的主要因素之一——跨多个物理存储区对数据进行分区。