第2章 データ取り込みとロード / 想定学習時間:30〜40分 / 最終確認:2026年8月

2-6. 取り込み方式の使い分けと半構造化データ(JSON・ネスト)の取り込み

🎯 この節の学習目標

1. 使い分けの判断軸

2-1 から 2-5 で個々の取り込み手段を学びました。本節ではそれらを「どう選ぶか」の視点で総まとめします。判断軸は次の4つです。

判断軸問うこと効いてくる選択
データ量ファイル数・行数はどの規模か。今後増えるか数千ファイルまでなら COPY INTO、それ以上・増加傾向なら Auto Loader
頻度1回きりか、定期か、継続的か1回きり→アップロード UI や CTAS、定期→スケジュール実行、継続→ストリーミング
データタイプ / ソースファイルか、SaaS か、DB か、API か。構造化か半構造化かファイル→COPY INTO / Auto Loader、SaaS・DB→マネージドコネクタ、API→自前 REST
ガバナンス要件権限・監査・リネージをどこまで統合したいかいずれの方式でも宛先は UC 管理テーブル。接続・認証情報も UC / シークレットで管理

2. 優先順位の判断表

ソース種別ごとに「まず何を検討するか」を優先順位つきでまとめます。試験のシナリオ問題は、ほぼこの表のどこかに着地します。

ソース / 状況第1候補第2候補備考
クラウドストレージ:大規模・継続的・スキーマ進化ありAuto LoaderLakeflow Connect 標準コネクタチェックポイントで増分管理、file notification で大規模化に対応
クラウドストレージ:数千ファイル・SQL 中心・定時バッチCOPY INTOAuto Loader(availableNow)SQL だけで完結。冪等でリトライ安全
SaaS アプリケーションLakeflow Connect マネージドコネクタパートナーコネクタどちらも未対応なら REST 自前実装
リレーショナルデータベースLakeflow Connect マネージドコネクタJDBC 自前実装JDBC の場合は watermark 列で増分抽出を自作
メッセージバス(Apache Kafka・Amazon Kinesis 等)からのストリーミングLakeflow Connect 標準コネクタ(Structured Streaming で自分で構成)低遅延の継続取り込み。マネージドコネクタの対象外
REST API のみ提供のソースrequests + createDataFrameLakeflow Jobs でスケジュール。シークレットで認証管理
手元のファイルを1回だけアップロード UI(ファイルのアップロード)小規模・アドホック専用。運用パイプラインには使わない
手元に繰り返し届くファイルUnity Catalog Volume + COPY INTO / Auto LoaderVolume に置けばストレージ経由と同じ増分取り込みに乗る(2-1)
ソースは何か?
SaaS・データベース
マネージドコネクタ対応?対応→マネージドコネクタ/未対応→パートナー→自前(JDBC/REST)
クラウドストレージのファイル
規模と性質は?大規模・継続的・スキーマ進化→Auto Loader/数千規模・SQL・定時→COPY INTO
1回きりの手元ファイル
アップロード UI
いずれの場合も
宛先は UC 管理 Delta テーブルに統一

図:取り込み方式の選択フロー

📝 試験のポイント

シナリオ問題では、問題文の中のキーワードが方式を指し示します。「SQL のみ」「数千ファイル」→ COPY INTO。「数百万ファイル」「継続的に到着」「スキーマが変わる」→ Auto Loader。「SaaS」「コードを書かずに」「フルマネージド」→ マネージドコネクタ。「Kafka」「メッセージバス」「低遅延ストリーミング」→ 標準コネクタ(Structured Streaming)。「手元のファイルを1回だけ」→ アップロード UI、「手元に繰り返し届く」→ Volume + COPY INTO / Auto Loader。「コネクタ未対応の DB」→ JDBC。「HTTP API しかない」→ REST 自前実装。この対応表を反射的に引けるようにしておきましょう。

3. 半構造化データ:JSON のスキーマ推論

取り込み対象として最も扱いが問われるのが JSON です。JSON は行ごとに項目が異なりうる半構造化データであり、スキーマの決め方が論点になります。

3-1. schema hints:推論の部分上書き

schema hints は「スキーマ全体は推論に任せつつ、特定の列だけ型を指定する」仕組みです。全列のスキーマを書き切る手間なく、重要な列の型だけを固定できます。

(spark.readStream
  .format("cloudFiles")
  .option("cloudFiles.format", "json")
  .option("cloudFiles.schemaLocation", schema_path)
  .option("cloudFiles.schemaHints", "amount DECIMAL(10,2), event_date DATE")
  .load(source_path)
)

4. ネスト構造(struct / array)の取り込みと基本操作

JSON のネスト(入れ子)は、Spark では struct 型(名前つきフィールドの束)と array 型(同型要素の並び)の列として表現されます。

💡 具体例:ネストした JSON とテーブル上の表現

-- ソースの JSON(1行)
-- {"order_id": 1001,
--  "customer": {"id": "C-01", "region": "east"},
--  "items": [{"sku": "A-1", "qty": 2}, {"sku": "B-9", "qty": 1}]}

-- 取り込み後のスキーマ
-- order_id BIGINT
-- customer STRUCT<id: STRING, region: STRING>
-- items    ARRAY<STRUCT<sku: STRING, qty: BIGINT>>

-- struct のフィールドはドット記法で参照できる
SELECT
  order_id,
  customer.id     AS customer_id,
  customer.region AS region,
  size(items)     AS item_count
FROM main.bronze.orders_json;

customer.id のようにドット記法で struct の中へ入れます。array の要素を行に展開する explode などの本格的なネスト変換は、第3章(データ変換)で扱います。取り込み段階では「ネストのまま Bronze に着地させ、展開は後段で行う」のが基本方針です。

5. VARIANT 型:スキーマを決めずに受ける

スキーマが頻繁に変わる、あるいは事前に定義しきれない JSON に対しては、VARIANT 型という選択肢もあります。VARIANT は半構造化データをスキーマを固定せずそのまま格納する型で、文字列として保存するより効率的に、パス指定での参照ができます。

-- VARIANT 列として JSON を受ける
CREATE TABLE main.bronze.events_v (
  event_id BIGINT,
  payload  VARIANT
);

-- 文字列から VARIANT への変換と参照
SELECT
  parse_json(raw_json)                AS payload,
  payload:user.id::STRING             AS user_id,
  payload:metrics.duration_ms::BIGINT AS duration_ms
FROM staging_raw;

「struct / array に推論して受ける」か「VARIANT で受けて後から参照する」かは、スキーマの安定度で選びます。項目構成が安定しているなら推論(+schema hints)、変化が激しい・多様なイベントが混在するなら VARIANT が向きます。

✅ この節のまとめ

練習問題

問1. あるチームの要件:「クラウドストレージのランディングゾーンに1日あたり数十万ファイルの JSON が継続的に到着する。ソースの項目は時々増える。増分だけを取り込み、新しい項目にも追随したい。」最も適切な取り込み方式はどれか。

  1. COPY INTO を1時間ごとにスケジュール実行する
  2. Auto Loader(スキーマ進化を有効化、必要に応じて file notification モード)で取り込む
  3. アップロード UI で毎日まとめて取り込む
  4. JDBC でストレージに接続して読み取る
解答と解説を見る

正解:B

「大規模(数十万ファイル/日)・継続的・スキーマが変わる」は Auto Loader を指すキーワードの3点セットです。この規模では COPY INTO のファイル照合コストが問題になるためAは不適です。Cは手動運用でありアドホック用途専用です。Dは誤りで、JDBC はデータベース接続の仕組みであり、オブジェクトストレージには使いません。

問2. 別のチームの要件:「経理部門が使う SaaS 会計アプリケーションのデータを毎晩同期したい。チームは SQL アナリスト中心で、抽出コードの開発・保守は避けたい。対応するマネージドコネクタが提供されている。」最も適切な選択はどれか。

  1. Lakeflow Connect のマネージドコネクタで接続し、夜間スケジュールの同期を設定する
  2. SaaS の REST API に対する Python 抽出コードを書き、Lakeflow Jobs で実行する
  3. SaaS から手動で CSV をダウンロードし、アップロード UI で取り込む
  4. Auto Loader で SaaS アプリケーションを直接ソースに指定する
解答と解説を見る

正解:A

「SaaS・コードを書きたくない・対応コネクタあり」はマネージドコネクタの典型シナリオです。Bは技術的には可能ですが、開発・保守を避けたいという明示要件に反します(コネクタ未対応の場合の次善策です)。Cは毎晩の手動作業が発生し論外です。Dは誤りで、Auto Loader のソースはクラウドストレージ上のファイルであり、SaaS を直接指定することはできません。

問3. Auto Loader で JSON を取り込んだところ、金額の列 amount が文字列型として推論されてしまった。スキーマ全体は推論に任せたまま、amount 列だけ DECIMAL(10,2) にしたい。最も適切な方法はどれか。

  1. cloudFiles.schemaHints に "amount DECIMAL(10,2)" を指定する
  2. 全列のスキーマを手書きして .schema() で渡す
  3. cloudFiles.schemaEvolutionMode を rescue に変更する
  4. 取り込み後のテーブルで ALTER TABLE により型を直せば、以降の推論も変わる
解答と解説を見る

正解:A

schema hints は「推論に任せつつ特定列だけ型を指定する」ための仕組みで、この要件にちょうど合います。Bでも実現はできますが、全列を書き切る保守負担が生じ「スキーマ全体は推論に任せたい」という要件に反します。Cはスキーマ進化(新列への対応)の設定であり、既存列の型指定とは無関係です。Dは誤りで、ターゲット側の変更はソースの推論結果を変えません(そもそも型の不一致で書き込みに失敗する原因になります)。

問4. 取り込んだテーブルに customer という STRUCT 型の列があり、その中の region フィールドを SELECT で取り出したい。正しい書き方はどれか。

  1. SELECT explode(customer) FROM orders
  2. SELECT customer.region FROM orders
  3. SELECT JOIN customer ON region FROM orders
  4. STRUCT 型の中身は取り込み前にフラット化しない限り参照できない
解答と解説を見る

正解:B

struct のフィールドは 列名.フィールド名 のドット記法でそのまま参照できます。Aの explode は array を行に展開する関数で、struct のフィールド参照には使いません(第3章で扱います)。Cは構文として成立しません。Dは誤りで、ネストのまま Bronze に着地させて後からドット記法で参照するのは標準的なやり方です。

問5. イベント種別ごとに項目構成がばらばらで、今後も頻繁に変わる JSON ペイロードを取り込む。スキーマを固定せずに格納し、必要なときにパス指定で値を取り出したい。最も適切な格納方法はどれか。

  1. すべての可能な列を NULL 許容で定義した巨大なスキーマを事前に作る
  2. VARIANT 型の列に格納し、payload:path 形式で参照する
  3. JSON を平文の STRING 列に入れ、毎回正規表現で切り出す
  4. 項目構成が安定するまで取り込みを延期する
解答と解説を見る

正解:B

「スキーマを固定できない・変化が激しい・パス参照したい」は VARIANT 型の設計意図そのものです。Aは変更のたびにスキーマ改修が必要になり、目的に反します。Cも動きはしますが、型情報を失い、性能・可読性ともに VARIANT に劣ります(VARIANT は文字列格納より効率的に参照できます)。Dはビジネス要件を満たさない先送りにすぎません。