Ezekielx
Ezekielx
发布于 2025-11-24 / 4 阅读
0
0

Hive Chapter 3:HQL Syntax(HQL 语法)

Hive Chapter 3:HQL Syntax(HQL 语法)

一、HQL Syntax(HQL 语法)

Hive 提供了一种机制,可以在 Hadoop 中的数据之上投射结构,并使用一种类似 SQL 的语言——HiveQL (HQL) 来查询这些数据。

之所以使用 Hive,是因为 Hive 中的表与关系型数据库中的表非常相似。如果你熟悉 SQL,那么使用 Hive 就会非常容易。

许多用户可以同时使用 Hive-QL 来查询数据。

在本章中,将使用以下 Hive 表为例:

员工编号 (emp_id) 姓名 (name) 性别 (gender) 年龄 (age) 入职季度 (Q_O_J) 入职年份 (Y_O_J) 在公司年限 (A_I_C) 薪水 (salary) 上次涨薪幅度 (L_H) country region
100 Bernard M 55.98 Q3 2017 0.02 10000 13% India South
101 Cordia F 25.19 Q1 2014 3.54 12000 15% US South
102 Burton M 41.43 Q3 2004 12.85 13000 30% England South
103 Lauren M 38.32 Q3 2001 15.94 15000 1% US North
104 Carter M 51.75 Q2 2014 3.27 14000 5% England North
105 Isaiah M 57.98 Q3 2004 12.93 11000 2% India North
106 Hugh M 37.65 Q4 2014 2.69 9000 30% India West
107 Lucius M 45.21 Q4 2003 13.69 8000 2% US South
108 Deane F 53.24 Q1 1999 18.36 7000 9% US West
109 Joannie F 33.72 Q2 2012 5.27 6000 8% US South
110 Christene F 26.32 Q1 2013 4.37 15000 30% England South

上述的 “emp” 表包含以下列及其数据类型:

列名 (Column) 数据类型 (Data Type)
emp_id int
name string
gender string
age double
Q_O_J string
Y_O_J string
A_I_C double
Salary double
L_H double
Country string
Region string

SELECT 语句语法

SELECT [ALL | DISTINCT] select_expr, select_expr, ...
FROM table_reference
[WHERE where_condition]
[GROUP BY col_list [HAVING condition]]
[ 
	CLUSTER BY col_list | [DISTRIBUTE BY col_list] [SORT BY | ORDER BY col_list]
]
[LIMIT number]
  • ALL 与 DISTINCT

    用于区分查询结果中是否包含重复记录。默认是 ALL,表示查询所有记录;

    DISTINCT 表示去除重复记录,只保留唯一值。

  • WHERE

    与传统 SQL 中的 WHERE 条件语句类似,用于筛选符合条件的记录。

  • ORDER BY 与 SORT BY

    • ORDER BY:用于全局排序,整个结果集在一个 Reducer 中排序,因此只有一个 Reduce 任务。
    • SORT BY:用于局部排序,每个 Reducer 内部都会进行排序,可使用多个 Reducer 并行处理,效率更高。
  • LIMIT

    用于限制查询返回的记录条数。

  • 打印列名

    可以通过设置以下参数来打印查询结果的列名:

    set hive.cli.print.header = true;
    

WHERE 子句

示例:

hive> SELECT name, age 
FROM emp 
WHERE gender = 'M' AND salary > 9000;

结果:

OK
Bernard 55.98
Burton  41.43
Lauren  38.32
Carter  51.75
Isaiah  57.98
Time taken: 4.455 seconds, Fetched: 5 row(s)

ALL 与 DISTINCT 子句

ALLDISTINCT 用于指定查询结果中是否包含重复的行。

如果未明确指定,默认是 ALL(返回所有行);

DISTINCT 表示去除重复行,仅返回唯一结果。

**示例 1:**默认使用 ALL(查询全部记录):

hive> SELECT gender, country FROM emp;

结果:

OK
M       India
F       US
M       England
M       US
M       England
M       India
M       India
M       US
F       US
F       US
F       England
Time taken: 0.15 seconds, Fetched: 11 row(s)

**示例 2:**使用 DISTINCT(去重):

hive> SELECT DISTINCT gender, country FROM emp;

结果:

OK
F       England
F       US
M       England
M       India
M       US
Time taken: 75.705 seconds, Fetched: 5 row(s)

GROUP BY 子句

GROUP BY 用于配合聚合函数,对结果集按一个或多个列分组。

它可以对相同属性的记录进行聚合统计。

**示例 1:**按性别分组统计人数:

hive> SELECT gender, COUNT(*) 
FROM emp 
GROUP BY gender;

结果:

OK
F       4
M       7
Time taken: 59.711 seconds, Fetched: 2 row(s)

**示例 2:**按国家分组求工资总和:

hive> SELECT country, SUM(salary) 
FROM emp 
GROUP BY country;

结果:

OK
England 42000.0
India   30000.0
US      48000.0
Time taken: 63.935 seconds, Fetched: 3 row(s)

HAVING 子句

HAVING 类似于 WHERE,但它与 GROUP BY 搭配使用,用于筛选分组后的结果。

示例

hive> SELECT gender, COUNT(*) 
FROM emp 
GROUP BY gender 
HAVING gender = 'F';

结果:

OK
F       4
Time taken: 66.17 seconds, Fetched: 1 row(s)

LIMIT 子句

当查询结果返回大量记录时,可以使用 LIMIT 来限制返回的行数。

示例:

hive> SELECT name, age 
FROM emp 
LIMIT 3;

结果:

OK
Bernard 55.98
Cordia  25.19
Burton  41.43
Time taken: 0.128 seconds, Fetched: 3 row(s)

Column Alias 列别名

可以使用 AS 关键字为列定义别名,使查询结果更简洁。

示例:

hive> SELECT name, salary AS sal 
FROM emp 
LIMIT 2;

结果:

OK
Bernard 10000.0
Cordia  12000.0
Time taken: 0.11 seconds, Fetched: 2 row(s)

LIKE 与 RLIKE 运算符

LIKE(模糊匹配):

LIKE 是标准 SQL 运算符,用于模糊搜索字符串。

Hive 中的 LIKE 支持以下通配符:

  • '_' :匹配单个字符
  • '%' :匹配零个或多个字符

示例:

hive> SELECT emp_id, name, salary 
FROM emp 
WHERE name LIKE '_u%';

结果:

OK
102     Burton  13000.0
106     Hugh    9000.0
107     Lucius  8000.0
Time taken: 0.188 seconds, Fetched: 3 row(s)

RLIKE(正则匹配):

RLIKE 是 Hive 的扩展函数,基于 Java 正则表达式

它判断:如果 A 的任意子串能匹配 B,则返回 true。

LIKE 不同,RLIKE 不需要使用 % 通配符。

示例:

hive> SELECT emp_id, name, salary 
FROM emp 
WHERE name RLIKE '(Hugh|Deane)';

结果:

OK
106     Hugh    9000.0
108     Deane   7000.0
Time taken: 0.131 seconds, Fetched: 2 row(s)

1、Hive Set Operators(Hive 集合操作符)

Hadoop Hive 支持以下集合操作符。

UNION [DISTINCT] 或 UNION:

UNION 集合操作符用于合并两个查询结果,并去除重复的行。

UNIONUNION DISTINCT 等价,默认会对结果去重。

UNION ALL:

UNION ALL 用于合并两个查询结果,但不会去除重复的行。

相比 UNIONUNION ALL 执行更快,因为它省去了去重的计算过程。

Hive 中 UNION 与 UNION ALL 的语法

SELECT Statement
{ UNION [DISTINCT] | UNION [ALL] }
SELECT Statement;

Hive 子查询(Subqueries)

在 Hive 中,一个查询语句内部包含另一个查询语句的情况称为 子查询 (Subquery)。主查询(Main Query)依赖子查询返回的结果进行操作。

Hive 中的子查询主要分为两种类型:

FROM 子句中的子查询:

FROM 中使用子查询通常是为了生成一个临时结果集,再从中查询。

SELECT col1 
FROM (SELECT a + b AS col1 FROM t1) t2;

此处,子查询 (SELECT a + b AS col1 FROM t1) 被命名为临时表 t2,外层查询再从中取数据。

WHERE 子句中的子查询:

WHERE 中使用子查询通常是为了根据另一张表的结果进行筛选。

SELECT name, age 
FROM emp 
WHERE emp.emp_id IN (SELECT id FROM dept);

此处,只有当 emp.emp_id 存在于 dept 表的 id 列中时,记录才会被选中。

使用场景:

  • 获取来自不同表的两个列值组合而成的特定值
  • 处理一个表中数据依赖于另一个表中值的情况
  • 在多表之间进行列值的比较检查

分区查询(Partition-Based Query)

通常情况下,一个 SELECT 查询会扫描整个表(除非使用采样)。但如果表在创建时使用了 PARTITIONED BY 子句,则 Hive 会根据查询中指定的分区条件,仅扫描表的一部分数据。这称为 分区裁剪(Partition Pruning),能显著提高查询效率。

**示例 1:**在 WHERE 中指定分区范围:

假设 page_views 表按照 date 列分区:

SELECT page_views.* 
FROM page_views 
WHERE page_views.date >= '2008-03-01' 
  AND page_views.date <= '2008-03-31';

此查询只会扫描日期在 2008-03-01 到 2008-03-31 之间的分区,而不会读取整个表。

**示例 2:**在 JOIN 的 ON 子句中指定分区条件:

page_views 表与其他表(例如 dim_users)进行连接时,也可以在 ON 子句中直接加入分区条件:

SELECT page_views.* 
FROM page_views 
JOIN dim_users 
ON (page_views.user_id = dim_users.id 
    AND page_views.date >= '2008-03-01' 
    AND page_views.date <= '2008-03-31');

这样 Hive 会在执行连接(JOIN)前就对 page_views 进行分区裁剪,从而提升查询效率。

2、Joins in Hive(Hive中的连接操作)

在 Hive 中,Join(连接) 的作用与传统关系型数据库(RDBMS)中的相同。

Join 用于根据一个公共字段或值,从两个或多个表中提取有意义的数据

换句话说,Join 用于将多个表的数据组合在一起

当在 FROM 子句中指定了多个表时,就会执行一次 Join 操作。

目前,Hive 只支持基于“等值条件(equality conditions)”的连接,它不支持任何基于非等值条件(non-equality conditions) 的连接操作。

Using Equality Joins to Combine Tables(使用等值连接来组合表)

Hive 支持表与表之间的等值连接(Equality Join),以便将两个表中的数据组合在一起。

Hive 的语法如下:

SELECT table_fields
FROM table_one
JOIN table_two
ON (table_one.key_one = table_two.key_one
AND table_one.key_two = table_two.key_two);
SQL 语句 说明
SELECT table_fields 关键字,用于从两个表中选择一系列字段。
FROM table_one & JOIN table_two 列出要连接的两个表,用于获取 table_fields
ON (table_one.key_one = table_two.key_one``AND table_one.key_two = table_two.key_two) 列出连接两个表的等值条件规则。

Joining Tables in Hive(在 Hive 中连接表)

这段练习让你在 Hive 中创建两个表 census.personnamecensus.address 之间的连接(Join)。

示例使用脚本 Script_EqualJoin.txt

完整脚本如下:

-- 切换到 census 数据库
USE census;

-- 创建 personname 表,存储人员信息
CREATE TABLE census.personname (
  persid int,            -- 人员ID(主键,用于连接)
  firstname string,      -- 名字
  lastname string        -- 姓氏
)
CLUSTERED BY (persid) INTO 1 BUCKETS     -- 按 persid 分桶(这里仅 1 个桶)
STORED AS orc                           -- 使用 ORC 格式存储(高效压缩)
TBLPROPERTIES ('transactional' = 'true'); -- 设置表为可事务表(支持 ACID)

-- 向 personname 表中插入示例数据
INSERT INTO TABLE census.personname VALUES
  (0, 'Albert', 'Ape'),
  (1, 'Bob', 'Burger'),
  (2, 'Charlie', 'Clown'),
  (3, 'Danny', 'Drywer');

-- 创建 address 表,存储人员对应的邮政信息
CREATE TABLE census.address (
  persid int,          -- 人员ID(与 personname 表关联)
  postname string      -- 邮政编码
)
CLUSTERED BY (persid) INTO 1 BUCKETS     -- 同样按 persid 分桶
STORED AS orc
TBLPROPERTIES ('transactional' = 'true');

-- 向 address 表中插入示例数据
INSERT INTO TABLE census.address VALUES
  (1, 'KA13'),
  (2, 'KA9'),
  (10, 'SW1');

现在,你拥有两个表:

  • census.personname
  • census.address

接下来执行连接操作:

-- 执行连接查询
-- 目的:找出在两个表中都存在的人员(根据 persid 匹配)
SELECT
  personname.firstname,   -- 取出名字
  personname.lastname,    -- 取出姓氏
  address.postname        -- 取出邮政编码
FROM
  census.personname
JOIN
  census.address
ON (personname.persid = address.persid); -- 等值连接条件

连接结果如下:

OK
Bob Burger KA13
Charlie Clown KA9

Using Outer Joins(使用外连接)

Hive 支持在表之间使用 LEFT(左外连接)RIGHT(右外连接)FULL OUTER(全外连接)等值连接(Equality Join),用于处理连接键(key)没有匹配记录的情况。

Hive 的语法如下:

SELECT table_fields
FROM table_one [LEFT | RIGHT | FULL OUTER] JOIN table_two
ON (table_one.key_one = table_two.key_one
AND table_one.key_two = table_two.key_two);
SQL 语句 说明
SELECT table_fields 关键字,用于从两个表中选择一系列字段。
FROM table_one``LEFT JOIN table_two 列出要连接的两个表。LEFT JOIN(左外连接)会返回左表中所有匹配的记录,以及右表中匹配和不匹配的记录。
FROM table_one``RIGHT JOIN table_two 列出要连接的两个表。RIGHT JOIN(右外连接)会返回右表中所有匹配的记录,以及左表中匹配和不匹配的记录。
FROM table_one``FULL OUTER JOIN table_two 列出要连接的两个表。FULL OUTER JOIN(全外连接)会返回两个表中所有匹配的记录,以及左右两边未匹配的记录。
ON (table_one.key_one = table_two.key_one``AND table_one.key_two = table_two.key_two) 指定连接两个表的等值条件。

Joining Tables in Hive Using Left Join(在 Hive 中使用左连接连接表)

Hive 支持表之间的 等值连接(Equality Join),用于将两个表中的数据组合在一起。

本示例使用脚本 Script_OuterJoin.txt

完整脚本如下:

USE census;

SELECT personname.firstname,
       personname.lastname,
       address.postname
FROM
  census.personname
LEFT JOIN
  census.address
ON (personname.persid = address.persid);

该查询的结果应包含 4 条记录

OK
Albert Ape     NULL
Bob Burger     KA13
Charlie Clown  KA9
Danny Drywer   NULL

Joining Tables in Hive Using Right Join(在 Hive 中使用右连接连接表)

接下来执行一个 右连接(Right Join) 的示例:

SELECT personname.firstname,
       personname.lastname,
       address.postname
FROM
  census.personname
RIGHT JOIN
  census.address
ON (personname.persid = address.persid);

该查询的结果应包含 3 条记录

OK
Bob Burger     KA13
Charlie Clown  KA9
NULL NULL      SW1

Full Outer Join(在 Hive 中使用全外连接)

现在来看看 全外连接(Full Outer Join) 的示例:

SELECT personname.firstname,
       personname.lastname,
       address.postname
FROM
  census.personname
FULL OUTER JOIN
  census.address
ON (personname.persid = address.persid);

该查询的结果应包含 5 条记录

OK
Albert Ape      NULL
Bob Burger      KA13
Charlie Clown   KA9
Danny Drywer    NULL
NULL NULL       SW1

Joining Three Tables in One MapReduce(在一次 MapReduce 中连接三个表)

这个练习展示了如何在 一次 MapReduce 操作中连接三张表

示例使用脚本 Script_MultiJoin.txt

完整脚本如下:

-- 使用 census 数据库
USE census;

-- 创建账户表 account,用于存储人员账户余额信息
CREATE TABLE census.account (
  persid int,    -- 人员ID(用于与其他表连接)
  bamount int    -- 账户余额(balance amount)
)
CLUSTERED BY (persid) INTO 1 BUCKETS   -- 按人员ID分桶(分桶字段必须与连接键一致)
STORED AS orc                          -- 使用 ORC 格式存储(支持事务和高效压缩)
TBLPROPERTIES('transactional' = 'true'); -- 启用事务支持,使表具有 ACID 特性

-- 向 account 表插入测试数据
-- 表示每个人员ID对应的账户余额
INSERT INTO TABLE census.account VALUES
  (1, 12),    -- persid = 1,对应账户余额 12
  (2, 9);     -- persid = 2,对应账户余额 9
-- 注意:这里只插入两个ID,与 personname 和 address 表的数据部分匹配

-- 执行三表连接查询
-- 在同一个 MapReduce 作业中完成多表 Join
SELECT
  personname.firstname,   -- 来自 personname 表的“名”
  personname.lastname,    -- 来自 personname 表的“姓”
  address.postname,       -- 来自 address 表的邮政编码
  account.bamount         -- 来自 account 表的账户余额
FROM
  census.personname
JOIN
  census.address
ON (personname.persid = address.persid)   -- 第一层连接:人员表与地址表,通过 persid 匹配
JOIN
  census.account
ON (personname.persid = account.persid);  -- 第二层连接:人员表与账户表,通过 persid 匹配
-- 由于两个 JOIN 都基于同一个键(persid),Hive 会优化为单个 MapReduce 阶段执行

查询结果应包含 2 条记录

OK
Bob Burger     KA13  12
Charlie Clown  KA9   9

Using Largest Table Last(最大表后使用)

Hive 在执行多表连接(Join)时,会先缓存前面的表,然后将最后一个表通过 Map 阶段与前面缓存的数据进行匹配。

Hive 的语法如下:

SELECT table_one.key_one,
       table_two.key_one,
       table_three.key_one
FROM
  table_one
JOIN
  table_two
ON (table_one.key_one = table_two.key_one)
JOIN
  table_three
ON (table_three.key_one = table_two.key_one);
SQL 语句 说明
table_onetable_two 缓存在内存中(Buffered in memory)
table_three 直接从磁盘映射读取(Mapped directly from di

Hive 的第二种语法如下:

SELECT table_one.key_one, 
       table_two.key_one, 
       table_three.key_one
FROM 
  table_one
JOIN 
  table_three
ON (table_one.key_one = table_three.key_one)
JOIN 
  table_two
ON (table_two.key_one = table_three.key_one);
SQL 语句 说明
table_onetable_three 缓存在内存中(Buffered in memory)
table_two 直接从磁盘映射读取(Mapped directly from disk)

3、ORDER BY and SORT BY(ORDER BY 与 SORT BY)

在 Hive 中操作数据的另一个重要方面,是对数据或结果集进行正确的排序(order/sort),以便清晰地识别出关键信息,例如 前 N 个值(Top N)最大值最小值 等。

Hive 中用于对数据进行排序和排列的关键字如下:

ORDER BY(升序 ASC / 降序 DESC)

该语句与传统关系型数据库(RDBMS)中的 ORDER BY 功能相同。

Hive 在执行 ORDER BY 时,会对所有 Reducer 输出的数据进行全局排序,即在所有结果中保持完整的有序性。

由于 ORDER BY 只使用一个 Reducer 来执行全局排序,因此在数据量较大的情况下,执行时间会较长。

建议在使用 ORDER BY 时,最好结合 LIMIT 一起使用,以限制返回的数据量,从而提高性能。

SELECT name
FROM emp
ORDER BY name DESC;

SORT BY(升序 ASC / 降序 DESC)

SORT BY 用于指定在将数据发送给 Reducer 之前,Mapper 端应该根据哪些列进行排序。也就是说,排序在发送数据到 Reducer 之前完成

SORT BY 不会进行全局排序(Global Sort),它只保证每个 Reducer 内部的数据是局部有序(Locally Sorted)。除非设置参数 mapred.reduce.tasks=1,否则结果不会像 ORDER BY 那样在全局范围内有序。

如果将 mapred.reduce.tasks 设置为 1,那么 SORT BY 的结果将与 ORDER BY 完全相同。

  • 使用多个 Reducer:
SET mapred.reduce.tasks = n;   -- n 可以是 2, 3 等多个 Reducer
SELECT name FROM emp SORT BY name;
  • 使用单个 Reducer:
SET mapred.reduce.tasks = 1;
SELECT name FROM emp SORT BY name;

DISTRIBUTE BY

DISTRIBUTE BY 用于指定具有相同列值的行应被分配到同一个 Reducer 中。当单独使用 DISTRIBUTE BY 时,并不能保证 Reducer 输入的数据是有序的

在功能上,它类似于关系型数据库(RDBMS)中的 GROUP BY,即决定了 Mapper 的输出将被发送到哪个 Reducer。

当与 SORT BY 一起使用时,DISTRIBUTE BY 必须出现在 SORT BY 之前。同时,用于分发的列必须出现在 SELECT 的列列表中。

  • 单独使用:
SELECT name, emp_id
FROM emp
DISTRIBUTE BY emp_id;
  • SORT BY 一起使用:
SELECT name, emp_id
FROM emp
DISTRIBUTE BY emp_id
SORT BY name;

CLUSTER BY

CLUSTER BY 是一种简写操作符,相当于同时执行 DISTRIBUTE BYSORT BY,并且基于同一组列完成分发与排序。

CLUSTER BY 在每个 Reducer 内部进行局部排序,但目前不支持 ASC 或 DESC(即只能默认升序)。

ORDER BY 的区别是:

  • ORDER BY 进行全局排序(Global Sort)
  • CLUSTER BY 只在每个 Reducer 内部排序(Local Sort)

在需要全局排序但又想充分利用多个 Reducer 时,可以先使用 CLUSTER BY 再执行 ORDER BY

SELECT name, emp_id
FROM emp
CLUSTER BY name;

idginHFB-1.png

4、HQL common examples(HQL 常用示例)

  • 创建表

    create table emp(
        emp_id string,
        name string,
        gender string,
        age double,
        Q_O_J string,
        Y_O_J string,
        A_I_C double,
        salary double,
        L_H double,
        country string,
        region string
    ) 
    row format delimited 
    fields terminated by ',' 
    lines terminated by '\n' 
    stored as textfile;
    
  • 查询薪资最高的员工姓名

    select name 
    from (select * from emp order by salary desc limit 1) a;
    
  • 输出每个国家(country)中的区域(region)数量

    select country, count(*) 
    from emp 
    group by country;
    
  • 输出拥有区域数最多的两个国家

    select country 
    from (
        select * 
        from (
            select country, count(*) as cnt 
            from emp  
            group by country
        ) a 
        order by cnt desc 
        limit 2
    ) b;
    
  • 输出薪资第二高的员工姓名

    select name 
    from (
        select * 
        from (
            select * from emp order by salary desc limit 2
        ) a 
        order by salary 
        limit 1
    ) b;
    
  • 将查询结果导出到本地

    INSERT OVERWRITE LOCAL DIRECTORY '/opt/datafiles' 
    select name 
    from (
        select * 
        from (
            select * from emp order by salary desc limit 2
        ) a 
        order by salary 
        limit 1
    ) b;
    
  • 查询销售记录最大的 5 个销售代表

    SET mapred.reduce.tasks = 1;
    
    SELECT * 
    FROM test 
    SORT BY amount DESC 
    LIMIT 5;
    
  • 从同一张表向不同的表和路径插入数据

    FROM src
    INSERT OVERWRITE TABLE dest1 
        SELECT src.* WHERE src.key < 100
    
    INSERT OVERWRITE TABLE dest2 
        SELECT src.key, src.value WHERE src.key >= 100 and src.key < 200
    
    INSERT OVERWRITE TABLE dest3 PARTITION(ds='2008-04-08', hr='12') 
        SELECT src.key WHERE src.key >= 200 and src.key < 300
    
    INSERT OVERWRITE LOCAL DIRECTORY '/tmp/dest4.out' 
        SELECT src.value WHERE src.key >= 300;
    
  • 输出总薪资(salary)最低的两个国家

    select * 
    from (
        select country, sum(salary) as sal  
        from emp 
        group by country
    ) a 
    order by sal 
    limit 2;
    

二、Hive Views(Hive 视图)

基本上,Apache Hive 的视图与 Hive 表类似,它们都是根据需求生成的。

idginHFB-2.png

1、Apache Hive View on External Table(基于外部表的 Apache Hive 视图)

  • 作为 Hive 视图,我们可以保存任何结果集数据。
  • 从用途上来说,它的使用方式与 SQL 中的视图相同。
  • 此外,我们可以对 Hive 视图执行所有类型的 DML 操作。

换句话说,Apache Hive 的视图是数据库中的一个可查询对象,它是由查询语句定义的。然而,我们不能在视图中存储数据。视图通常被称为 “虚拟表”。因此,我们可以像查询表一样查询视图。另外,通过使用 joins,可以将多个表中的数据组合起来。视图也可以只包含部分数据(数据子集)。

Apache Hive 视图语法

CREATE VIEW <VIEWNAME> AS SELECT ...

创建 Hive 视图

在执行 SELECT 语句的同时,我们可以创建视图。因此,创建 Hive 视图的语法如下:

CREATE VIEW [IF NOT EXISTS] view_name 
    [(column_name [COMMENT column_comment], …)]
    [COMMENT table_comment]
AS SELECT …

Apache Hive 视图示例

假设有一张员工表(employee),包含字段:Id、Name、Salary、Designation 和 Dept。

现在,我们要查询工资高于 RMB 35000 的员工,并将结果存储在名为 emp_35000 的视图中。

示例数据如下:

ID Name Salary Designation Dept
1201 Michel 45000 Technical manager TP
1202 Chandler 45000 Proofreader PR
1203 Ross 40000 Technical writer TP
1204 Joey 40000 Hr Admin HR
1205 Monika 35000 Op Admin Admin

根据以上数据,创建视图的 Hive 查询如下:

hive> CREATE VIEW emp_35000 AS 
       SELECT * FROM employee 
       WHERE salary > 35000;

删除 Hive 视图

要删除 Hive 视图,可以使用以下语法:

DROP VIEW view_name;

例如,删除名为 emp_35000 的视图:

hive> DROP VIEW emp_35000;

三、Advanced Table Properties(高级表属性)

在加载表数据时跳过表头(Header)和表尾(Footer)记录。

在许多源文件中,我们经常会看到文件顶部的一些行(header)和底部的一些行(footer)。这些行并不是实际数据,而是包含一些关于数据或文件本身的信息。

因此,我们有两种处理方式:

  • 手动去文件中删除这些表头和表尾行,然后再基于清理后的文件创建表(不推荐)。
  • 使用表属性(table properties)在创建表时自动跳过这些表头和表尾行(推荐)。

请看下面的源文件示例:

  • 文件的 前两行 是表头(header),提供文件的创建日期和文件名等信息。
  • 文件的 最后两行 是表尾(footer),提供文件总行数、最后修改日期等信息。
File Creation Date : 2024-01-10
File Name : employee_data.txt
101,Michael,45000,TP
102,Ross,40000,TP
103,Joey,40000,HR
104,Monika,35000,Admin
105,Chandler,45000,PR
Total Rows : 5
Last Modified : 2024-01-12

我们在创建表时使用了属性 "skip.header.line.count"="2",因为我们想要跳过文件顶部的 2 行;同时使用 "skip.footer.line.count"="2",因为我们也想跳过文件底部的 2 行。

create table if not exists Tb_Employee (
    id int,
    EMP_NAME string,
    Department_ID int,
    Manager_ID int,
    Designation string,
    Location string,
    Years_of_Experience double
)
row format delimited 
fields terminated by ',' 
lines terminated by '\n' 
stored as textfile 
tblproperties (
    "skip.header.line.count"="2",
    "skip.footer.line.count"="2"
);

现在,当我们向这个表中加载数据时,Hive 会自动跳过前两行的表头和后两行的表尾内容。

1、Hive Immutable Table property(Hive 不可变表属性)

不可变对象是指一旦创建就不能被修改的对象。Hive 提供了表的不可变属性,用于阻止 insert into 命令对表进行追加操作。insert into 命令用于向表或分区追加数据,同时保留已有数据。

在正常情况下,当表不是不可变(即表是可变的 mutable)时,insert into 的行为是:如果一张表中已经存在数据,现在希望在保留旧数据的前提下插入新数据,就会使用 insert into

Mutable Table(可变表):所有表默认都是可变的。当表中已存在数据时,可变表允许继续追加数据。

Immutable Table(不可变表):可以通过将表属性设置为 true 来创建不可变表。默认情况下该属性为 false。

创建不可变表示例:

create table if not exists EMP1 (
    emp_id int,
    emp_name string,
    dept_id int
)
row format delimited 
fields terminated by ',' 
lines terminated by '\n' 
stored as textfile 
tblproperties ("immutable"="true");

不可变属性允许首次加载数据,但在第一次插入之后,再执行后续的 insert into 会失败,因此不可变表只能拥有一份数据集。

对非空的不可变表执行 insert into:如果尝试这样做,Hive 会报错,表示不允许向非空的不可变表插入数据。

对非空的不可变表执行 insert overwrite:不可变表中所有旧数据会被删除,并插入新数据。

因此可以得出结论:insert overwrite 不受不可变属性的影响。

2、Hive Null Format property(Hive 空值格式属性)

这个属性允许将传入的指定值视为 NULL。下面来看一个示例。

下面的源文件包含 3 列,但其中有 3 行(第 2、3、5 行)第二列的值缺失。例如在第 2 行,20 后面的第二列值缺失:

20,20-April-2021,Shanghai
30,,Guiyang
40,,Tongran
50,21-May-2021,Delhi
60,22-June-2021,Mumbai

如果我们尝试将这个文件加载到一张具有 3 列的 Hive 表中,Hive 会成功加载文件。但是,如果我们尝试从表中查询第二列为 NULL 的数据,将会返回零行记录。

这是因为在默认情况下,分隔符之间没有任何内容时,Hive 并不会将其视为 NULL。在 Hive 看来,只要是在分隔符之间的内容,不管是否为空,它都会当作普通数据,而不会当成 NULL。

示例表:

create table if not exists DEPT_DET (
    Department_ID int,
    Started_on string,
    Location string
)
row format delimited 
fields terminated by ',' 
lines terminated by '\n' 
stored as textfile;

未使用 Null Format 属性的表中显示的数据:

hive> select * from dept_det;
OK
20  20-April-2021  Shanghai
30      Guiyang
40      Tongran
50  21-May-2021    Delhi
60  22-June-2021   Mumbai

如上所示,Hive 没有将空字段填成 NULL。

使用 Null Format 属性的表中显示的数据:

让我们创建另一张表。这里使用表属性 "serialization.null.format"=""(空字符串),表示我们希望将空字段替换为 NULL。
如果我们想让空格也变成 NULL,只需要在设置属性时把等号后面的值改成一个空格即可。

示例:

create table if not exists DEPT_DET1 (
    Department_ID int,
    Started_on string,
    Location string
)
row format delimited 
fields terminated by ',' 
lines terminated by '\n' 
stored as textfile 
tblproperties("serialization.null.format"="");

查询结果:

hive> select * from dept_det1;
OK
20      20-April-2021   Shanghai
30      NULL            Guiyang
40      NULL            Tongran
50      21-May-2021     Delhi
60      22-June-2021    Mumbai

如上所示,Hive 现在会将空字段替换为 NULL。设置此属性后,Hive 会将所有空值都视为 NULL。


评论