作者:IvanCodes
日期:2025年5月20日
專欄:Hive教程
在Hive中,我們經常需要以不同于原始表結構的方式查看或處理數據。為了簡化復雜查詢、提供數據抽象,以及處理復雜數據類型(如數組或Map),Hive 提供了視圖 (View) 和 Lateral View 這樣強大的機制。
思維導圖
一、Hive 視圖 (View):數據的邏輯窗口
Hive 視圖是一個虛擬表,它的內容由一個查詢定義。視圖本身不存儲任何物理數據,而是在被查詢時動態執行其定義的SELECT語句,并返回結果。
視圖的優勢:
- 簡化復雜性:將多表連接、復雜函數、聚合等封裝在視圖中,用戶只需查詢簡單的視圖。
- 數據抽象:隱藏底層表的物理細節。若底層表結構改變,只需修改視圖定義,上層應用可能無需變動。
- 訪問控制:可以創建只暴露部分列或符合特定條件行的視圖,增強數據安全性。
- 邏輯重用:相同的查詢邏輯定義一次,多處使用,便于維護。
基本視圖操作:
(1) 創建視圖 (CREATE VIEW)
CREATE VIEW [IF NOT EXISTS] view_name
[(column_list)]
[COMMENT view_comment]
AS SELECT_statement;
示例: 假設有 employees
(id, name, department_id, salary) 和 departments
(id, name) 表。
CREATE VIEW employee_department_details_view
COMMENT 'Shows employee name, salary, and their department name'
AS
SELECT e.name AS employee_name, e.salary, d.name AS department_name
FROM employees e
JOIN departments d ON e.department_id = d.id;
(2) 查詢視圖
SELECT * FROM employee_department_details_view WHERE salary > 60000;
(3) 查看視圖定義
SHOW CREATE TABLE employee_department_details_view;
(4) 修改視圖 (ALTER VIEW)
ALTER VIEW [db_name.]view_name AS SELECT_statement;
格式:
ALTER VIEW employee_department_details_view AS
SELECT e.name AS emp_name, e.salary, d.name AS dept_name, e.id AS emp_id -- 修改了列名并增加了列
FROM employees e
JOIN departments d ON e.department_id = d.id;
(5) 刪除視圖 (DROP VIEW)
DROP VIEW [IF EXISTS] [db_name.]view_name;
格式:
DROP VIEW IF EXISTS employee_department_details_view;
二、Lateral View:行轉列的魔法
有時,我們的Hive表中會包含數組 (ARRAY) 或 映射 (MAP) 這樣的復雜數據類型。如果我們希望將這些集合類型中的每個元素或鍵值對 “展開” 成單獨的行,以便進行更細致的分析,這時就需要Lateral View。
Lateral View 通常與表生成函數 (UDTF, User-Defined Table-generating Function) 一起使用,最常用的 UDTF 就是 explode()
。explode()
函數可以接收一個數組或Map作為輸入,并為數組中的每個元素或 Map中的每個鍵值對輸出一行。
Lateral View 的工作方式:
Lateral View 會首先將 UDTF 應用于基表的每一行。然后,它將UDTF的輸出行與原始的輸入行進行連接 (join),形成新的虛擬表行。
語法:
SELECT ...
FROM base_table
LATERAL VIEW udtf(expression) table_alias AS column_alias_1 [, column_alias_2, ...];
udtf(expression)
: 表生成函數及其參數,如explode(array_column)
或explode(map_column)
。table_alias
: 為 Lateral View 生成的虛擬表指定的別名。column_alias_1, ...
: 為 UDTF 輸出的列指定的別名。explode(array)
只輸出一列,explode(map)
輸出兩列 (key, value)。
示例1:展開數組
假設有一個表 user_hobbies
(user_id INT, hobbies ARRAY)。
-- 假設 user_hobbies 表數據:
-- 1, ['reading', 'hiking']
-- 2, ['coding', 'gaming', 'reading']SELECT user_id, single_hobby
FROM user_hobbies
LATERAL VIEW explode(hobbies) exploded_hobbies_table AS single_hobby;
查詢結果將會是:
1, reading
1, hiking
2, coding
2, gaming
2, reading
示例2:展開Map
假設有一個表 product_attributes
(product_id INT, attributes MAP<STRING, STRING>)。
-- 假設 product_attributes 表數據:
-- 101, {'color':'red', 'size':'M'}
-- 102, {'material':'cotton', 'brand':'XYZ'}SELECT product_id, attr_key, attr_value
FROM product_attributes
LATERAL VIEW explode(attributes) exploded_attributes_table AS attr_key, attr_value;
查詢結果將會是:
101, color, red
101, size, M
102, material, cotton
102, brand, XYZ
三、在視圖定義中使用 Lateral View
Lateral View 的強大之處在于它可以被包含在視圖的 AS SELECT
定義中。這樣,我們就可以創建一個視圖來永久性地提供這種展開后的數據展現形式。
示例:創建一個視圖來展示每個用戶的單個愛好
CREATE VIEW user_individual_hobbies_view AS
SELECT user_id, single_hobby
FROM user_hobbies
LATERAL VIEW explode(hobbies) exploded_hobbies_table AS single_hobby;-- 后續查詢
SELECT * FROM user_individual_hobbies_view WHERE user_id = 1;
四、視圖(含Lateral View)的特性與注意事項
- 非物化:Hive 視圖(包括使用了 Lateral View 的視圖)默認不存儲實際數據。每次查詢時,都會重新執行其定義,確保數據最新。
- 只讀性:通常不能通過視圖對底層表進行
INSERT
,UPDATE
,DELETE
操作。 - 性能考量:復雜的視圖定義,特別是涉及多個Lateral View或深層嵌套時,可能會影響查詢性能。需要關注底層表的優化 (如分區、分桶)。
ORDER BY
限制:視圖定義中的SELECT
不推薦直接使用ORDER BY
(除非配合LIMIT
)。排序應在最終查詢視圖時應用。
總結: Hive 視圖提供了數據的邏輯抽象層,而 Lateral View 則是處理和轉換數組、Map等復雜結構的強大工具。將兩者結合使用,可以極大地增強數據分析和展現的靈活性與便捷性。
練習題
背景數據表:
products
(product_id INT, product_name STRING, category STRING, price DECIMAL(8,2), tags ARRAY)sales
(sale_id INT, product_id INT, sale_date STRING, quantity INT, customer_id INT, sale_details MAP<STRING, STRING>)customers
(customer_id INT, customer_name STRING, city STRING)
請根據以下表結構和數據自行插入一些樣例數據用于測試。
例如:
products
表中一條數據: (1, 'Laptop X', 'Electronics', 1200.00, array('slim', 'powerful', '15-inch'))
sales
表中一條數據: (101, 1, '2023-01-15', 1, 201, map('channel','online', 'promo_code','SAVE10'))
題目:
- 創建一個視圖
product_basic_info_view
,顯示所有產品的product_id
,product_name
, 和price
。 - 創建一個視圖
product_tags_expanded_view
,將products
表中的tags
數組展開,每行顯示product_id
,product_name
和一個單獨的tag
。 - 創建一個視圖
sales_details_expanded_view
,將sales
表中的sale_details
Map展開,每行顯示sale_id
,product_id
,以及Map中的detail_key
和detail_value
。 - 創建一個視圖
electronics_product_tags_view
,只顯示類別 (category
) 為 ‘Electronics’ 的產品的product_name
和其展開后的每個tag
。 - 創建一個視圖
total_quantity_per_product_view
,顯示每個product_name
的總銷售數量 (total_quantity
)。 - 基于第5題的
total_quantity_per_product_view
,創建一個新視圖high_sales_products_view
,只顯示total_quantity
大于10的產品。 - 修改第2題創建的
product_tags_expanded_view
,使其額外顯示產品的category
。 - 查看
sales_details_expanded_view
的創建語句。 - 假設有一個視圖
customer_purchase_channels_view
,它通過展開sales.sale_details
(假設其中有 ‘channel’ key) 來顯示每個客戶 (customer_name
) 的購買渠道。請寫出創建這個視圖的SQL語句 (需要連接customers
和sales
表)。 - 刪除視圖
product_basic_info_view
。
答案
- 創建
product_basic_info_view
:
CREATE VIEW product_basic_info_view AS
SELECT product_id, product_name, price
FROM products;
- 創建
product_tags_expanded_view
:
CREATE VIEW product_tags_expanded_view AS
SELECT p.product_id, p.product_name, single_tag
FROM products p
LATERAL VIEW explode(p.tags) exploded_tags_table AS single_tag;
- 創建
sales_details_expanded_view
:
CREATE VIEW sales_details_expanded_view AS
SELECT s.sale_id, s.product_id, detail_key, detail_value
FROM sales s
LATERAL VIEW explode(s.sale_details) exploded_details_table AS detail_key, detail_value;
- 創建
electronics_product_tags_view
:
CREATE VIEW electronics_product_tags_view AS
SELECT p.product_name, single_tag
FROM products p
LATERAL VIEW explode(p.tags) exploded_tags_table AS single_tag
WHERE p.category = 'Electronics';
- 創建
total_quantity_per_product_view
:
CREATE VIEW total_quantity_per_product_view AS
SELECT p.product_name, SUM(s.quantity) AS total_quantity
FROM sales s
JOIN products p ON s.product_id = p.product_id
GROUP BY p.product_name;
- 創建
high_sales_products_view
:
CREATE VIEW high_sales_products_view AS
SELECT product_name, total_quantity
FROM total_quantity_per_product_view
WHERE total_quantity > 10;
- 修改
product_tags_expanded_view
:
ALTER VIEW product_tags_expanded_view AS
SELECT p.product_id, p.product_name, p.category, single_tag
FROM products p
LATERAL VIEW explode(p.tags) exploded_tags_table AS single_tag;
- 查看
sales_details_expanded_view
的創建語句:
SHOW CREATE TABLE sales_details_expanded_view;
- 創建
customer_purchase_channels_view
:
CREATE VIEW customer_purchase_channels_view AS
SELECT
c.customer_name,
details.detail_value AS purchase_channel
FROM
customers c
JOIN
sales s ON c.customer_id = s.customer_id
LATERAL VIEW explode(s.sale_details) details_table AS detail_key, detail_value
WHERE details.detail_key = 'channel';
- 刪除
product_basic_info_view
:
DROP VIEW IF EXISTS product_basic_info_view;