CSV ファイルから外部テーブルを作成する

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

はじめに

最初の Hive 実験では、準備済みのテーブルですぐに注文の行を検索できました。今度は小売業者から CSV ファイルが届きましたが、ストレージにファイルがあるだけでは、Hive が検索できるテーブルにはなっていません。フィールドを定義し、データのあるディレクトリを指定する必要があります。

外部テーブルは、その定義とは別に管理されるデータを参照するテーブル定義です。スキーマは列名と型を指定し、LOCATION は保存されたファイルを指します。定義を作成しても、ファイルのアップロードや内容の書き換えは行われません。Hive はテーブルのメタデータを Metastore に保存し、記録自体はストレージに残ります。

単純な CSV 入力を2つアップロードし、明示的なスキーマで最初の入力を対応付け、その参照先を2つ目に変更します。その後、練習用の外部テーブル定義だけを削除します。最後の実験では、このコースの Hive バージョンでデータ保持のプロパティを明示します。あらゆる Hive テーブルを削除してもファイルが残ると保証するものではありません。サービスとローカル入力は独立して準備済みです。Hadoop コースやプログラミング言語の知識は不要で、ターミナルを使います。

元ファイルを確認してアップロードする

このステップでは、CSV 形式を確認し、提供された2ファイルを別々の HDFS ディレクトリにコピーします。

ローカルのプロジェクトから始めます。

cd /home/labex/project

CSV ファイルはカンマでフィールドを区切ります。この学習用ファイルは、値にカンマ、引用符で囲んだ複数行のテキスト、エスケープした区切り文字を含まない単純なものです。今回の単純なテーブル形式はこの入力に合わせたもので、あらゆる種類の CSV に対応する万能の解析方法ではありません。

ローカルの head -n 4 は先頭4行を表示します。

head -n 4 /home/labex/project/data/orders-a.csv

ヘッダーと、それに続く3件の注文が表示されるはずです。

order_id,customer_id,product_id,quantity,unit_price,order_status,order_date
o1,c1,p1,2,10.00,paid,2026-10-14
o2,c2,p2,1,25.00,paid,2026-10-14
o3,c1,p1,1,10.00,pending,2026-10-14

wc -l で、改行で終わる行を数えます。

wc -l /home/labex/project/data/orders-a.csv /home/labex/project/data/orders-b.csv

入力 A は7行ですが、業務上の記録は6件です。入力 B は3行ですが、記録は2件です。ヘッダーを注文行にしてはいけません。

HDFS は、準備済みの Hive サービスが使う別のファイルシステムです。hdfs dfs クライアントでファイルを管理します。-mkdir -p は指定した HDFS ディレクトリと不足する親を作成します。

hdfs dfs -mkdir -p /user/labex/hive-mapping/source-a /user/labex/hive-mapping/source-b

-put はローカルの元ファイルを HDFS の保存先にコピーし、元ファイルを残します。

hdfs dfs -put /home/labex/project/data/orders-a.csv /user/labex/hive-mapping/source-a/orders.csv

2つ目の入力を専用のディレクトリにアップロードします。

hdfs dfs -put /home/labex/project/data/orders-b.csv /user/labex/hive-mapping/source-b/orders.csv

成功した場合、通常は何も表示されません。保存先が存在する場合は、上書きを判断する前に確認してください。コマンドを繰り返すためだけに元データを消してはいけません。-cat で最初の HDFS ファイルを読みます。

hdfs dfs -cat /user/labex/hive-mapping/source-a/orders.csv

ヘッダーと6件の記録がそのまま残っています。アップロードはバイト列を保持し、テーブル定義がその解釈方法を Hive に伝えます。

ファイルとテーブルの対応を定義する

このステップでは、フィールド、型、ディレクトリの場所、ヘッダー処理を明示して外部テーブルを作ります。

元のフィールドの順番と、テーブルの列の順番は一致する必要があります。識別子と状態には STRING、整数の数量には INT、固定小数点の価格には DECIMAL(10,2)、ISO 形式の日付には DATE を使います。価格を Decimal にすると、後の金額計算で二進浮動小数点による近似を避けられます。

CREATE DATABASE IF NOT EXISTS は、必要なら名前空間を作成します。続く CREATE EXTERNAL TABLE はテーブル定義を登録します。書式の句でカンマ区切りのテキストを指定し、LOCATION にはディレクトリを指定します。その中に無関係なファイルやバックアップを置かないでください。2つのプロパティは、ファイルごとにヘッダー1行を読み飛ばし、この環境で DROP 時の外部テーブルデータの削除を明示的に無効にします。

完全な SQL ファイルを保存します。最初の実験と同じく、シェルのヒアドキュメントが、最後の SQL 区切りまでの行をそのまま書き込みます。

cat > /home/labex/project/queries/create-orders.sql <<'SQL'
CREATE DATABASE IF NOT EXISTS retail_mapping;
USE retail_mapping;
CREATE EXTERNAL TABLE raw_orders (
  order_id STRING,
  customer_id STRING,
  product_id STRING,
  quantity INT,
  unit_price DECIMAL(10,2),
  order_status STRING,
  order_date DATE
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE
LOCATION 'hdfs://localhost:9000/user/labex/hive-mapping/source-a'
TBLPROPERTIES ('skip.header.line.count'='1', 'external.table.purge'='false');
SQL

Beeline で実行します。接続とファイル用の -f オプションは、最初の実験と同じです。

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true -f /home/labex/project/queries/create-orders.sql

これらは DDL(Data Definition Language:データ定義言語)の文で、テーブルなどのオブジェクトを定義する SQL です。DDL が成功すると、注文行を返すのではなく、メタデータを作成します。完了済みの実行によって表がすでにある場合は、メッセージを消すために削除せず、内容を調べてください。

DESCRIBE FORMATTED は、列だけでなくテーブルの種類と場所も含めて表示します。

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex -e 'DESCRIBE FORMATTED retail_mapping.raw_orders;'

EXTERNAL_TABLE と、/hive-mapping/source-a で終わる場所を探してください。これはメタデータの観察で、すべての記録が正しく解析できた証拠ではありません。クエリでも確かめます。COUNT(*) はテーブルの行を数えるので、対応付けの簡単な確認になります。AS order_count は計算した結果に列名を付けます。これは別名(エイリアス)と呼ばれ、元のフィールドの名前や保存済みの記録は変えません。

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex -e 'SELECT COUNT(*) AS order_count FROM retail_mapping.raw_orders; SELECT order_id, quantity, unit_price, order_date FROM retail_mapping.raw_orders ORDER BY order_id LIMIT 2;'

件数は、テキストの7行ではなく 6 のはずです。最初の2件には ID o1 と o2、数量 2 と 1、数値として 10.00 と 25.00 に等しい価格、日付 2026-10-14 が含まれるはずです。ヘッダー処理と、意味のある型付きの列の両方を確認できました。

同じテーブルの参照先を新しい入力に変える

このステップでは、どちらの元ファイルも移動せずに、テーブルの場所を変え、行がどう変わるか予想します。

入力 B は同じフィールドを持ちますが、注文は o7 と o8 です。メタデータを変える前に、保存されたファイルを読みます。

hdfs dfs -cat /user/labex/hive-mapping/source-b/orders.csv

ヘッダーと2件の記録があるはずです。このディレクトリを参照させた後の行数を予想してください。6件ではなく2件です。ALTER TABLE ... SET LOCATION は、テーブル定義のディレクトリ参照を変更します。古いデータを新しいディレクトリにコピーしたり、2回分のデータを結合したりはしません。

メタデータの変更を保存します。

cat > /home/labex/project/queries/map-second-source.sql <<'SQL'
ALTER TABLE retail_mapping.raw_orders SET LOCATION 'hdfs://localhost:9000/user/labex/hive-mapping/source-b';
SQL

実行します。

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true -f /home/labex/project/queries/map-second-source.sql

メタデータとデータの両方を再確認します。

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex -e 'DESCRIBE FORMATTED retail_mapping.raw_orders; SELECT COUNT(*) AS order_count FROM retail_mapping.raw_orders; SELECT order_id, quantity, unit_price FROM retail_mapping.raw_orders ORDER BY order_id;'

新しい source-b の場所、件数 2、o7 と o8 の行を探してください。数量は 1 と 2、価格は数値として 50.00 と 15.00 です。列定義は変わらず、それを通じて読むファイルだけが変わりました。

古いファイルを読み、対応を変えても削除されていないことを確かめます。

hdfs dfs -cat /user/labex/hive-mapping/source-a/orders.csv

6件の記録とヘッダーが残っているはずです。ファイルが存在することと、表から見えることは別です。今の表は入力 B を読みますが、入力 A も引き続き存在しています。

練習用のテーブル定義だけを削除する

このステップでは、保持した最初の入力に対して外部テーブル定義を作り、それを削除します。データと主テーブルは保ちます。

テーブル定義の削除と、データの削除は別の操作です。この実験では、別名 disposable_orders を使い、外部データの保持プロパティを明示します。入力 B を参照する raw_orders は、作業中も利用できるままにします。

既知の同じスキーマを使い、入力 A に対する練習用定義を作ります。

cat > /home/labex/project/queries/create-disposable.sql <<'SQL'
CREATE EXTERNAL TABLE retail_mapping.disposable_orders (
  order_id STRING,
  customer_id STRING,
  product_id STRING,
  quantity INT,
  unit_price DECIMAL(10,2),
  order_status STRING,
  order_date DATE
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE
LOCATION 'hdfs://localhost:9000/user/labex/hive-mapping/source-a'
TBLPROPERTIES ('skip.header.line.count'='1', 'external.table.purge'='false');
SQL

定義を実行します。

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex --silent=true -f /home/labex/project/queries/create-disposable.sql

両方のテーブル名があることを確認します。

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex -e 'SHOW TABLES IN retail_mapping;'

disposable_orders と raw_orders があるはずです。DROP TABLE はテーブル定義を削除します。ここでは、purge プロパティを明示的に false にした練習用の外部テーブルだけを対象にします。

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex -e 'DROP TABLE retail_mapping.disposable_orders; SHOW TABLES IN retail_mapping;'

raw_orders だけが残るはずです。次に、元ファイルを独立して確認します。

hdfs dfs -cat /user/labex/hive-mapping/source-a/orders.csv

ヘッダーと6件の記録は残るはずです。この観察結果は、今回の外部テーブルの設定に対するものです。管理テーブルや、purge プロパティの異なる外部テーブルでは、DROP 時にデータが削除されることがあります。この実験を、すべての削除に当てはまるルールと考えてはいけません。

主テーブルが引き続き入力 B を読むことを確かめます。

beeline -u 'jdbc:hive2://localhost:10000/default' -n labex -e 'SELECT order_id FROM retail_mapping.raw_orders ORDER BY order_id;'

引き続き o7 と o8 が表示されるはずです。初心者が混同しやすい、バイト列のアップロード、メタデータの登録・変更、定義の削除という3操作を区別できました。次は、正しく対応付けた行を使い、条件で絞り込んだり欠損値を扱ったりします。

まとめ

単純な CSV 入力をアップロードし、フィールドの順番と型、ヘッダーの読み飛ばしを定義して、明示した HDFS ディレクトリに外部テーブルを登録しました。メタデータと実際の記録の両方を確かめ、どちらの元ファイルも移動せずに、テーブルの参照先を変更しました。

最後に、保持設定を明示した練習用の外部テーブル定義を削除し、ファイルと主テーブルが利用可能なままであることを確認しました。次は、この信頼できる対応付けを土台に、行の選択と欠損値の処理を学びます。