介绍
终端里显示的查询只能供当次使用。要让其他查询复用报表,就需要存储结果。你将把零售商的每日已付款销售查询转换成一个小型 Hive 表,并在再次请求相同报表时安全重建它。
源数据是可读的 CSV 文本。ORC(Optimized Row Columnar)是二进制列式存储格式:它在文件结构内按列组织值,可支持高效分析读取。它不是另一种 SQL 语言,也不能替代表模式。这里的小型数据用来展示正确存储和复用,不用于证明速度提升。
本实验独立在外部文本表中准备六个干净订单。Hive 已就绪。你应该了解外部表、SELECT、已付款销售聚合和日期分组。这里将介绍写入结果的语句,使用预配置环境支持的明确非事务表。无需编程、安装或之前实验的文件。请使用终端。
定义具有明确类型的 ORC 报表表
在这一步中,你将创建空目标表,使其模式与预期报表一致。与 CSV 源表一样,目标也明确是外部表,并有已知 HDFS 位置。它的格式是 ORC 而不是文本。外部表描述元数据与文件的关系,不表示表是只读的。Hive 可以向其中写入 SELECT 结果。结果位置独立于源数据,因此重建报表不会替换原始订单。
进入工作目录:
cd /home/labex/project
输出每行包含一个日期、一个计数和一个金额总计。COUNT(*) 生成 BIGINT 计数。DECIMAL(14,2) 允许总计十四位十进制数字,其中两位在小数点后;对于这个小报表足够使用。明确的模式有助于避免意外将金额存成文本。
保存并执行定义。here-document 将标记之间的 SQL 原样写入;Beeline 的 -f 对预配置服务运行该文件:
cat > /home/labex/project/queries/create-orc-report.sql <<'SQL'
CREATE EXTERNAL TABLE IF NOT EXISTS retail_orc.daily_sales (
order_date DATE,
paid_orders BIGINT,
revenue DECIMAL(14,2)
)
STORED AS ORC
LOCATION 'hdfs://localhost:9000/warehouse/external/retail_orc.db/daily_sales'
TBLPROPERTIES ('transactional'='false', 'external.table.purge'='false');
SQL
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -f /home/labex/project/queries/create-orc-report.sql
STORED AS ORC 选择格式。LOCATION 指定预配置数据库可写仓库目录下的新结果目录。external.table.purge=false 在外部定义被删除时保留文件,但不会阻止明确的覆盖操作替换报表行。transactional=false 请求非事务表。Hive 显示元数据时可能省略这个 false 属性;只有 true 值才表示事务表。本实验不讲 ACID 更新或删除。查看元数据和空行数:
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "DESCRIBE FORMATTED retail_orc.daily_sales;" > /home/labex/project/output/orc-definition.txt
cat /home/labex/project/output/orc-definition.txt
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "SELECT COUNT(*) FROM retail_orc.daily_sales;" > /home/labex/project/output/empty-report.tsv
cat /home/labex/project/output/empty-report.tsv
找到三种列类型、外部表类型,以及 ORC 输入/输出格式名称。预期为 0 行:定义目标没有运行销售计算,也没有复制任何 CSV 数据。接下来,你将明确填充它。
写入并读回每日报表
在这一步中,你将运行已付款销售 SELECT,并持久化其输出。INSERT OVERWRITE TABLE 用 SELECT 结果替换目标表当前内容,不会覆盖正在读取的外部订单表。所选值按顺序分配给目标列:日期、计数、收入。
保存完整重建脚本。CREATE TABLE IF NOT EXISTS 允许在正确表定义已存在时再次执行定义,但不会修复错误模式。后面的 INSERT 执行实际报表构建:
cat > /home/labex/project/queries/build-sales-report.sql <<'SQL'
CREATE EXTERNAL TABLE IF NOT EXISTS retail_orc.daily_sales (
order_date DATE,
paid_orders BIGINT,
revenue DECIMAL(14,2)
)
STORED AS ORC
LOCATION 'hdfs://localhost:9000/warehouse/external/retail_orc.db/daily_sales'
TBLPROPERTIES ('transactional'='false', 'external.table.purge'='false');
INSERT OVERWRITE TABLE retail_orc.daily_sales
SELECT order_date, COUNT(*) AS paid_orders,
SUM(quantity * unit_price) AS revenue
FROM retail_orc.orders
WHERE order_status = 'paid'
GROUP BY order_date;
SQL
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -f /home/labex/project/queries/build-sales-report.sql
成功完成表示写入结束。请通过查询存储的 ORC 表来确认,而不只是重跑原始源数据 SELECT:
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "SELECT order_date, paid_orders, revenue FROM retail_orc.daily_sales ORDER BY order_date;" > /home/labex/project/output/stored-report.tsv
cat /home/labex/project/output/stored-report.tsv
预期为 10 月 14 日 / 2 / 45.00,以及 10 月 15 日 / 2 / 80.00。金额显示可能带末尾零。这是两条已存储结果行,汇总四个已付款源订单;不是原始 CSV 六条记录的复制品。
ORC 文件是二进制的,因此使用 cat 查看字节不会生成可读 CSV。对它执行 SELECT 时,Hive 会解释表模式和文件格式。源文件仍是普通文本,保留全部六个订单,包括两条被排除的状态。
重建时不累积重复日期
在这一步中,你将测试可重复性。预测再次执行相同覆盖脚本后的行数。目标应该仍有两个日期,而不是四条复制的汇总行:
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -f /home/labex/project/queries/build-sales-report.sql
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "SELECT order_date, paid_orders, revenue FROM retail_orc.daily_sales ORDER BY order_date;" > /home/labex/project/output/rebuilt-report.tsv
cat /home/labex/project/output/rebuilt-report.tsv
报表应该仍是 10 月 14 日 / 2 / 45.00,以及 10 月 15 日 / 2 / 80.00。直接从结果表检查简短对账:
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "SELECT COUNT(*), SUM(paid_orders), SUM(revenue) FROM retail_orc.daily_sales;" > /home/labex/project/output/report-reconciliation.tsv
cat /home/labex/project/output/report-reconciliation.tsv
预期为 2 条存储报表行、4 笔已付款源订单、125.00 收入。结果行数和 paid_orders 总和描述不同的明细层级。
在本地比较保存的前后数据。两个 SELECT 都使用 ORDER BY,因此数据行顺序稳定。Beeline 可能包含客户端消息,所以有意义的比较对象是带日期的结果行和数值对账,而不是消息时间戳。
对于不变的输入,这种重建具有幂等性:重复执行会留下相同报表内容。接下来,一个临时目标将展示写入模式为什么重要。
在临时副本上比较追加和覆盖
在这一步中,你将在独立表中有意复制一个小型派生报表。主表 daily_sales 保持正确报表。使用相同的明确模式创建临时表:
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "CREATE EXTERNAL TABLE IF NOT EXISTS retail_orc.append_demo (
order_date DATE,
paid_orders BIGINT,
revenue DECIMAL(14,2)
)
STORED AS ORC
LOCATION 'hdfs://localhost:9000/warehouse/external/retail_orc.db/append_demo'
TBLPROPERTIES ('transactional'='false', 'external.table.purge'='false');"
INSERT INTO TABLE 将 SELECT 结果追加到已有内容。将两行主报表追加两次:
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "INSERT INTO TABLE retail_orc.append_demo SELECT order_date, paid_orders, revenue FROM retail_orc.daily_sales;"
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "INSERT INTO TABLE retail_orc.append_demo SELECT order_date, paid_orders, revenue FROM retail_orc.daily_sales;"
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "SELECT COUNT(*), SUM(paid_orders), SUM(revenue) FROM retail_orc.append_demo;" > /home/labex/project/output/append-observation.tsv
cat /home/labex/project/output/append-observation.tsv
预期为 4 行、合计 8 笔已付款订单、250.00 收入。额外行代表重复报表副本,不是新销售。没有新订单到来;追加完整重建结果导致了错误业务结果。
使用之前学过的写入模式,将临时内容替换为一份副本:
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "INSERT OVERWRITE TABLE retail_orc.append_demo SELECT order_date, paid_orders, revenue FROM retail_orc.daily_sales;"
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "SELECT order_date, paid_orders, revenue FROM retail_orc.append_demo ORDER BY order_date;" > /home/labex/project/output/overwrite-observation.tsv
cat /home/labex/project/output/overwrite-observation.tsv
它恢复为两个正确日期行。做一个小练习,再重复一次覆盖:
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "INSERT OVERWRITE TABLE retail_orc.append_demo SELECT order_date, paid_orders, revenue FROM retail_orc.daily_sales;"
beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true --showHeader=false --outputformat=tsv2 -e "SELECT COUNT(*), SUM(revenue) FROM retail_orc.append_demo;"
预期再次为 2 行和 125.00。真正添加新的、不重叠的数据时,追加很有用;重建完整快照则需要替换语义。保存的主构建脚本使用覆盖,因此能够重建完整报表,不会悄悄累积重复日期。
总结
你定义了有明确类型的非事务 ORC 结果表,写入每日已付款销售总计,读回结果,并测试了可重复重建。临时追加实验展示了重复报表副本如何夸大收入,以及覆盖如何恢复完整快照。接下来,你将在最终报表挑战之前诊断常见文件到表映射错误。



