Hive クエリの結果を ORC テーブルに保存する

ビッグデータBeginner
オンラインで実践に進む

はじめに

ターミナルに表示したクエリは、その場で役立ちます。ほかのクエリから再利用するレポートには、保存した結果が必要です。小売業者の支払い済み日次売上クエリを小さな Hive テーブルにし、同じレポートを再度求められたときも安全に再構築します。

元データは読みやすい CSV テキストです。ORC(Optimized Row Columnar)はバイナリの列指向保存形式です。ファイル構造の中で列ごとに値を整理し、分析の効率的な読み取りを支援できます。別の SQL 言語でも、テーブルスキーマの代わりでもありません。今回の小さなサンプルは、速度向上の実証ではなく、正しい保存と再利用を学ぶためのものです。

この実験では、外部テキストテーブルに正常な6注文を独立して準備します。Hive は使用可能です。外部テーブル、SELECT、支払い済み売上の集計、日付のグループ化を理解していることを想定します。結果を書き込む文はここで学び、この準備済み環境が対応する、明示的な非トランザクションテーブルを使います。プログラミング、インストール、以前の実験のファイルは不要です。ターミナルを使ってください。

型付きの ORC レポートテーブルを定義する

このステップでは、目的のレポートに合うスキーマの空の保存先を作ります。CSV 入力と同じく、既知の HDFS の場所を持つ明示的な外部テーブルです。形式はテキストではなく ORC です。外部という言葉はメタデータとファイルの関係を表し、読み取り専用という意味ではありません。Hive は SELECT の結果を書き込めます。結果の場所は元データと別なので、レポートを再構築しても元の注文は置き換わりません。

作業ディレクトリから始めます。

cd /home/labex/project

出力は1行に日付、件数、金額合計を持ちます。COUNT(*) は BIGINT の件数を返します。DECIMAL(14,2) は、全体14桁、うち小数点以下2桁の十進数を確保し、この小さなレポートには十分です。スキーマを明示すると、金額を誤ってテキストとして保存するのを防ぐ助けになります。

定義を保存して実行します。ヒアドキュメントは区切りの間の 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

3列の型、外部テーブルの種類、ORC の入出力形式名を探します。行数は0のはずです。保存先を定義しただけでは売上計算は実行されず、CSV データもコピーされません。次に明示的にデータを入れます。

日次レポートを書き込み、読み戻す

このステップでは、支払い済み売上の SELECT を実行し、出力を保存します。INSERT OVERWRITE TABLE は、保存先テーブルの現在の内容を SELECT の結果で置き換えます。読み取り元の外部 orders テーブルは上書きしません。選択した値は、日付、件数、売上の順に保存先列に割り当てられます。

完全な再構築スクリプトを保存します。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

正常終了は書き込みの完了を示します。元の入力 SELECT を再実行するだけでなく、保存した ORC テーブルを検索して確認します。

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 が期待されます。金額表示には末尾の0が付く場合があります。これは支払い済み入力4注文を要約した保存結果2行であり、元の CSV 記録6件のコピーではありません。

ORC はバイナリファイルなので、cat でバイト列を見ても読みやすい CSV にはなりません。SELECT すると、Hive がテーブルのスキーマと形式を解釈します。元のファイルは通常のテキストのまま、除外した2つの状態も含む6注文すべてを保持します。

日付の重複を蓄積せずに再構築する

このステップでは、再現性を試します。同じ上書きスクリプトを再実行したときの行数を予想してください。保存先は集計行のコピー4行ではなく、引き続き2日分のはずです。

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 の結果を追記します。2行の主レポートを2回追記します。

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 が期待されます。増えた行は新しい売上ではなく、レポートの重複コピーです。注文は届いていないのに、完全な再構築結果を追記したことで、業務上の結果が間違ってしまいました。

先ほどの書き込みモードを使い、練習用の内容を1コピーで置き換えます。

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

正しい2つの日次行に戻ります。小さな確認練習として、その上書きをもう一度繰り返します。

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 結果テーブルを定義し、支払い済みの日次売上を書いて読み戻し、再現可能な再構築を試しました。練習用の追記実験では、レポートの重複が売上を膨らませることと、上書きで完全なスナップショットに戻せることを確認しました。次は、最後のレポート作成チャレンジに進む前に、ファイルとテーブルの対応でよくある間違いを診断します。