Data Engineer Associate — 第 7

取り込み時にスキーマをどう扱うか — Auto Loader のスキーマ進化と COPY INTO

読了 27確認 9更新 2026-08-09

この章で学ぶこと

  • Delta の既定はスキーマ強制だと確認する
  • スキーマ進化を許可する 4 つの手段を選び分ける
  • Auto Loader の推論上限と進化モードの既定値を覚える
  • rescuedDataColumn と schemaHints を使い分ける
  • COPY INTO の句とオプションを正しく書き分ける

この章に出てくる用語

スキーマ強制
書き込むデータの列構成が表定義とずれた状態を拒否する仕組みです。Delta Lake では既定で有効で、表にない列を含む DataFrame を append すると AnalysisException になります。
スキーマ進化
ソースに列が増えるたび手で表を直す手間をなくす仕組みです。Delta では mergeSchema を true にするか ALTER TABLE ADD COLUMNS を実行した場合だけ許可され、既定は無効です。
rescuedDataColumn
推論したスキーマに収まらない値を黙って捨てないための救済列です。既定名は _rescued_data で、未知の列名・型不一致・大文字小文字違いの値をソースファイルパスごと JSON で退避します。
cloudFiles.schemaHints
推論が STRING や DOUBLE に倒れて困る列だけを名指しで直すオプションです。値は SQL の DDL 表記で書き、user_info.dob DATE のような入れ子パスも指定でき、明示スキーマ時は効きません。
cloudFiles.schemaLocation
ストリームを再起動するたびにスキーマを推論し直す不安定さを消すための保存先です。推論したスキーマとその変遷が保存され、指定しないとスキーマ推論も進化モードも全く機能しない必須のオプションです。
COPY INTO
クラウドストレージ上のファイルを既存の Delta 表へ流し込む SQL コマンドです。取り込み済みのファイルはターゲット表側に記録されて次回はスキップされ、CSV や PARQUET など複数の形式を扱えます。
FORMAT_OPTIONS
CSV のヘッダー行や JSON の複数行など、ファイルの読み方だけを決める句です。値はそのまま Spark のリーダーへ渡され、header や multiLine や ignoreCorruptFiles を書きます。
冪等性
同じジョブを二度走らせても結果が変わらない性質です。COPY INTO は取り込み済みのファイルで判定し、Auto Loader はチェックポイント内の RocksDB で検出済みファイルを追跡して二重取り込みを防ぎます。

スキーマ強制と進化 — mergeSchema / autoMerge.enabled / overwriteSchema / ALTER TABLE ADD COLUMNS の使い分け

ソース側の担当者が JSON にフィールドを一つ足す。それだけで夜間バッチが朝には止まっている、あるいは黙って列が捨てられる。どちらが起きるかは書き込み先の設定で決まります。Delta Lake が既定で選んでいるのは「止まる方」で、これをスキーマ強制 (schema enforcement) と呼びます。この節では書き込み側の規則だけを片付けます。ファイルを読む側の話は次の節からです。

既定の挙動をコードで確認する

列が id と name の二つしかない Delta 表に、amount を足した DataFrame を追記してみます。

df_new = spark.createDataFrame(
    [(1, 'A', 100), (2, 'B', 200)],
    ['id', 'name', 'amount'])

df_new.write.format('delta') \
    .mode('append') \
    .save('/Volumes/main/raw/sales')

結果は `AnalysisException` です。amount が自動で追加されることはなく、amount が黙って捨てられることもありません。壊れた形のデータを下流へ流さないことを優先した設計で、試験では「mergeSchema を指定していない append の結果はどうなるか」という形で繰り返し問われます。答えは常に書き込みが失敗するです。「追加列は警告なしに無視される」「新列が自動で追加される」「テーブルが上書きされる」はすべて誤りの選択肢として並びます。

進化を許可する 4 つの手段

強制を恒久的に切るのではなく、この書き込みに限って進化を許すと宣言するのが Delta の作法です。手段は四つあり、効く範囲と危険度が違います。

手段書き方効く範囲と挙動既定
書き込みオプション`.option("mergeSchema", "true")`その 1 回の書き込みだけ。ソースにあってターゲットに無い列をスキーマ末尾に追加するfalse
セッション設定`spark.databricks.delta.schema.autoMerge.enabled`そのセッションの全書き込みに効く。Databricks はこの方法を本番で使うことを推奨していないfalse
上書きオプション`.option("overwriteSchema", "true")`overwrite 時にスキーマとパーティション定義ごと置き換える。動的パーティション上書き (書き込むデータに含まれるパーティションだけを差し替えるモード) とは併用できないfalse
DDL`ALTER TABLE ... ADD COLUMNS`データを書く前に表定義そのものを変える。`FIRST` や `AFTER 列名` で挿入位置を指定できる

ここで問われる判断は「安全に新しい列を足す方法を選べ」という形です。正解になるのは `mergeSchema` と `ALTER TABLE ... ADD COLUMNS` の二つで、`overwriteSchema` は表定義ごと差し替える破壊的な操作なので「安全な追加」の答えにはなりません。`ALTER TABLE` で追加した列は nullability が既定で true、つまり NULL を許す列になり、既存行の値は NULL のままです。

ALTER TABLE bronze.sales
  ADD COLUMNS (amount DECIMAL(18,2) COMMENT '税抜金額' AFTER name);

ここまでの四つはどれも列を足す方向にしか働きません。`mergeSchema` で追加された列は表スキーマの末尾に付きます。列の改名と削除をしたい場合は列マッピング (物理ファイル上の列名と論理的な列名を切り離す機能) を有効にしたうえで `ALTER TABLE ... RENAME COLUMN` と `ALTER TABLE ... DROP COLUMN` を使います。しかも `DROP COLUMN` はメタデータ上で列を消すだけで、ファイルの中のデータは残ります。物理的に消すには `REORG TABLE` のあとに `VACUUM` が要ります。セッション設定の `autoMerge.enabled` が本番向きでないのも同じ理由で、列名を打ち間違えたまま書き込むと、そのタイプミスがそのまま表定義として定着してしまいます。

`MERGE` と `INSERT` には専用の句があり、対応バージョンが別々なので数字だけ押さえます。SQL の `MERGE WITH SCHEMA EVOLUTION` 句は Databricks Runtime 15.2 以降、Python と Scala のテーブル API で `.withSchemaEvolution()` を挟む書き方は Databricks Runtime 15.4 LTS 以降、`INSERT WITH SCHEMA EVOLUTION` は `Databricks Runtime 18.1` 以降です。ここを取り違える設問が出ます。

スキーマ推論・スキーマ強制・スキーマ進化を混ぜない

この三つは名前が似ていますが、働く場所も、失敗したときに起きることも別です。混同したまま設問を読むと、選択肢が全部それらしく見えてしまいます。

用語働く場所何を決めるか典型的な設定
スキーマ推論読み取り側 (ファイルを読む瞬間)ファイルの中身から列名と型を組み立てる。JSON や CSV のように型情報を持たない形式で必要になる`cloudFiles.schemaLocation` / `cloudFiles.inferColumnTypes`
スキーマ強制書き込み側 (Delta 表に書く瞬間)表定義と違う形の DataFrame を拒否する。既定で有効設定不要 (常時働く)
スキーマ進化書き込み側強制の例外として、この書き込みに限り列の追加を認める`mergeSchema` / `ALTER TABLE ADD COLUMNS`

切り分けは症状で付きます。エラーメッセージに列名と型の対応表が出て書き込みが止まっているなら強制側、エラーは出ないのに全列が STRING になっているなら推論側です。読み取り側が新しい列を認識できていなければ、書き込み側で `mergeSchema` をいくら立てても列は増えません。スキーマ事故は、この二層のどちらで詰まっているかを決めるところから始まります。

強制をどの層でかけるか

メダリオンアーキテクチャ (Bronze・Silver・Gold の三層でデータを磨いていく設計) では、強制をかける場所も決まっています。Bronze はソースの生データをロスレスに残す層なので、パースと最小限の型付けだけにとどめ、検証は緩くします。正規化・重複排除・スキーマ強制と品質ルール (Expectations) は Silver 層に置きます。Bronze で厳格に弾くと、弾かれたデータが手元に残らず再処理ができなくなります。Delta 側のスキーマ進化そのもの、型の拡張、列マッピング、制約の詳細は当サイトの「Delta スキーマ進化」「Delta 制約」の記事で扱っているので、深掘りはそちらに送ります。

確認 — 穴あけ 3

0 / 3

空欄を押すと選択肢が出ます。間違えても減点はありません。

列を追加した DataFrame を mergeSchema なしで既存 Delta 表に append すると になります。

セッション全体でスキーマの自動マージを許可する設定は で、既定は false です。

SQL の MERGE WITH SCHEMA EVOLUTION 句が使えるのは Databricks Runtime 以降です。

Auto Loader (2) — スキーマ推論の上限(50GB / 1000ファイル)、schemaEvolutionMode 4値、rescuedDataColumn、cleanSource

Auto Loader を本番に載せたチームがまず驚くのは、金額のはずの列がすべて文字列になっていること、次に、上流が列を一つ足した翌朝にストリームが止まっていることです。どちらも設定を間違えたわけではなく、既定値どおりの動作です。この節では、その既定値を数値と値の名前で覚えます。試験がこの領域から出す問題は、ほとんどが「オプション名」「既定値」「モードの挙動」の三つに集約されます。

推論の材料は最初の 50 GB か 1000 ファイルしかない

Auto Loader は入力ディレクトリの全ファイルを読んでスキーマを決めているわけではありません。最初に検出した 50 GB か 1000 ファイルのうち、先に上限へ達した方までをサンプリングして推論します。1 億ファイルあるバケットでも、見るのは先頭のごく一部です。上限は次の二つの Spark 設定で変えられます。

設定意味既定
`spark.databricks.cloudFiles.schemaInference.sampleSize.numBytes`推論に使うバイト数の上限。`10gb` のようなバイト文字列で指定する50 GB
`spark.databricks.cloudFiles.schemaInference.sampleSize.numFiles`推論に使うファイル数の上限。整数で指定する1000

この上限があるせいで、サンプルに現れなかった列や、後から届いたファイルにしかない型は推論から漏れます。漏れた分をどう扱うかが、このあとの進化モードと救済列の役割です。

推論しても型がつかない — inferColumnTypes

JSON・CSV・XML のように型情報を持たない形式では、Auto Loader は入れ子フィールドを含む全列を文字列として推論します。「数値のはずの列が全部 STRING になった」という設問はこの仕様を突いたもので、原因は `cloudFiles.inferColumnTypes` が既定 false であることです。true にするとサンプルデータから具体的な型を推論します。schemaLocation を指定し忘れたせいではありませんし、救済列が型を奪っているわけでもありません。

推論結果の置き場所 — schemaLocation

スキーマ推論と進化を使うには `cloudFiles.schemaLocation` が必須です。指定したパスに推論したスキーマとその変遷が保存され、これを省くとストリーム再起動のたびに推論をやり直すことになり、進化モードも成立しません。

df = (spark.readStream.format("cloudFiles")
      .option("cloudFiles.format", "json")
      .option("cloudFiles.schemaLocation", "/Volumes/main/ops/_schemas/events")
      .option("cloudFiles.inferColumnTypes", "true")
      .option("cloudFiles.schemaHints", "amount DOUBLE, user_info.dob DATE")
      .option("cloudFiles.rescuedDataColumn", "_rescue")
      .load("/Volumes/main/raw/events"))

schemaEvolutionMode の 5 つの値

新しい列が現れたときに何をするかを決めるのが `cloudFiles.schemaEvolutionMode` です。長く 4 値と説明されてきましたが、型の拡張を伴うモードが加わって現在は 5 値あります。

新列を検出したときの挙動既定になる条件
`addNewColumns`スキーマに新列を追加したうえで `UnknownFieldException` を投げてストリームを止める。再起動すると新しいスキーマで取り込みを再開するスキーマを明示していないとき
`addNewColumnsWithTypeWidening`addNewColumns と同じ動きに加え、int から long のようなサポートされた型の拡張も行う。拡張できない変更は救済列へ回る
`rescue`スキーマを一切進化させない。新列はすべて救済列に入り、スキーマ変更でストリームが失敗することはない
`failOnNewColumns`失敗して停止する。人がスキーマを更新するか問題のファイルを取り除くまで再起動できない
`none`スキーマを進化させず、新しい列を無視する。`rescuedDataColumn` を設定していなければ救済もされないスキーマを明示したとき

引っかかりやすいのが `addNewColumns` と `failOnNewColumns` の区別です。どちらもストリームは落ちます。違うのは再起動したときで、addNewColumns は新列を取り込んで続きから走り、failOnNewColumns は人手でスキーマを直すまで走りません。Lakeflow Jobs (Databricks のジョブ実行基盤) のタスク再試行と addNewColumns を組み合わせると、人が介在せずに列追加を吸収できます。`PERMISSIVE` は後述するパーサーのモード名であって schemaEvolutionMode の値ではなく、誤答の常連です。

扱う範囲にも限りがあります。schemaEvolutionMode が面倒を見るのは列の追加と型の拡張までで、ソース側で列が削除されたことを検知して表から消す機能ではありません。また、ディレクトリ構造から取り出す Hive 形式のパーティション列は進化の対象外で、増やしたいときは `cloudFiles.partitionColumns` にカンマ区切りで明示します。

自分でスキーマを渡すと既定値が 3 か所裏返る

ここまでは推論に任せる前提でした。`.schema(...)` で自分のスキーマを渡すと、既定値が同時に三つ変わります。

設定推論に任せる場合スキーマを渡す場合
`schemaEvolutionMode``addNewColumns``none`
救済列`_rescued_data` が自動で付く`rescuedDataColumn` の指定が必要
`schemaHints`指定した列だけ型を固定適用されない

つまり固定スキーマにして何も足さないと、新しい列は進化もせず救済もされず静かに消えます。「スキーマを固定したらデータが欠けた」という障害報告の大半はこの組み合わせで、不具合ではありません。固定したまま取りこぼしを見えるようにするなら、`rescuedDataColumn` を明示するか、モードを `rescue` か `failOnNewColumns` に切り替えます。

救済列 — rescuedDataColumn

スキーマに収まらなかった値を捨てないための逃がし場所が救済列です。既定の列名は `_rescued_data` で、`cloudFiles.rescuedDataColumn` に別名を渡せば変えられます。中身は救済された列と、そのレコードのソースファイルパスを含む JSON です。対象になるのは、推論または宣言したスキーマに存在しない列、型が合わずキャストできなかった値、大文字小文字だけが違う列名の三種類です。

破損レコードの扱いとは別物なので混ぜないでください。JSON や CSV のリーダーには `mode` オプションがあり、既定は `PERMISSIVE`、壊れたレコードは `columnNameOfCorruptRecord` で指定した列 (既定名 `_corrupt_record`) に入ります。`DROPMALFORMED` で捨てられたり `FAILFAST` で落ちたりするのは、JSON や CSV として本当に壊れているレコードだけで、単なる型の食い違いはこちらではなく救済列に回ります。「想定外のカラムに値が入る」という現象の正体はたいてい救済列で、`SELECT _rescued_data FROM ...` で中身を確認できます。

schemaHints で型だけを直す

推論が STRING や DOUBLE に倒れて困る列を名指しで直すのが `cloudFiles.schemaHints` です。値は JSON ではなく SQL の DDL 表記で、`"amount DOUBLE, tags map<string,string>"` のようにカンマ区切りで並べ、`user_info.dob DATE` のような入れ子のパスも指定できます。指定しなかった列は通常どおり推論されます。ヒントを書いたのに型が変わらないときは、列名の綴り違いか別名で書いているのが原因なので、正しい「列名 型」で書き直し、schemaLocation を保ったまま再起動するのが定石です。チェックポイントを消してやり直すのは、再処理コストと重複を招くだけです。

処理済みファイルの後始末 — cleanSource

取り込みが終わったファイルを入力ディレクトリに置きっぱなしにすると、一覧取得のコストが延々と増えます。`cloudFiles.cleanSource` はこれを自動で片付けるオプションで、Databricks Runtime 16.4 以降で使えます。

オプション取りうる値・意味既定
`cloudFiles.cleanSource``OFF` / `DELETE` / `MOVE` の 3 択OFF
`cloudFiles.cleanSource.moveDestination`MOVE のときの退避先。ソースディレクトリの子であってはならず、ソースと同じ外部ロケーション・ボリューム・DBFS マウント内である必要があるなし
`cloudFiles.cleanSource.retentionDuration`処理済みになってから片付けの対象になるまでの猶予。`14 days` のように書く。DELETE では 7 日より長い値が必須30 days

MOVE の制約は言い換えると一つです。バケットやコンテナをまたぐ移動はサポートされません。

まとめて覚える既定値

オプション役割既定
`cloudFiles.maxFilesPerTrigger`1 トリガーで処理する新規ファイル数の上限1000
`cloudFiles.maxBytesPerTrigger`1 トリガーで処理するバイト数の上限。両方指定すると、どちらか一方の上限に達するのに必要なファイル数まで処理するなし
`cloudFiles.includeExistingFiles`ストリーム開始時点で既にあるファイルを対象に含めるかtrue
`cloudFiles.allowOverwrites`処理済みファイルが同名で上書きされたとき再処理するかfalse
`cloudFiles.validateOptions`未知のオプション名や矛盾する組み合わせを起動時に検証してエラーにするtrue
`cloudFiles.maxFileAge`重複排除のためにファイルイベントを追跡し続ける期間。短くしすぎると追跡から外れた古いファイルが再検出され重複取り込みになる。Databricks は 90 日程度の余裕を持たせることを勧めているなし
`cloudFiles.backfillInterval`ファイル通知の取りこぼしを補うバックフィルの間隔なし
`cloudFiles.useStrictGlobber`他の Spark ファイルソースと同じグロブ解釈に揃えるfalse

取り込み状況の確認には `cloud_files_state()` テーブル関数を使います。チェックポイントのパスか `TABLE(ストリーミングテーブル名)` を渡すと path・size・discovery_time・commit_time・ingestion_state などの列が返り、commit_time が NULL のファイルはまだ処理されていないことを意味します。ingestion_state には INGESTED や PROCESSING、SKIPPED_CORRUPTED といった値が入ります。バックログの大きさは Streaming Query Listener が報告する `numFilesOutstanding` と `numBytesOutstanding` で測ります。

確認 — 穴あけ 3

0 / 3

空欄を押すと選択肢が出ます。間違えても減点はありません。

スキーマを明示していないときの cloudFiles.schemaEvolutionMode の既定は です。

rescuedDataColumn に退避されるのは、未知の列・型不一致の値・ の三種類です。

Auto Loader のスキーマ推論は、最初に検出した のうち先に上限へ達した方までをサンプリングします。

COPY INTO 完全解剖 — 冪等性の記録先、FORMAT_OPTIONS と COPY_OPTIONS、VALIDATE / BY POSITION / FILES と PATTERN / WITH CREDENTIAL

SQL しか書かないチームが最初に困るのは、たいてい同じ場面です。ジョブが落ちたので手で流し直したら同じ行が二重に入った。毎晩ディレクトリをまるごと指定したいが、昨日のファイルまで読み直されるのが怖い。`COPY INTO` は、この「同じものを二度入れたくない」を SQL 一文で解決するコマンドです。Auto Loader がストリーミング側の道具なのに対して、COPY INTO はバッチの世界で冪等性を担保する道具です。

構文の全体像

COPY INTO target_table [ BY POSITION | ( col_name [, col_name ...] ) ]
FROM { source_clause | ( SELECT expression_list FROM source_clause ) }
FILEFORMAT = data_source
[ VALIDATE [ ALL | num_rows ROWS ] ]
[ FILES = ( file_name [, ...] ) | PATTERN = glob_pattern ]
[ FORMAT_OPTIONS ( { data_source_reader_option = value } [, ...] ) ]
[ COPY_OPTIONS ( { copy_option = value } [, ...] ) ]

source_clause
  source [ WITH ( [ CREDENTIAL ... ] [ ENCRYPTION ... ] ) ]

`FILEFORMAT` は公式ドキュメントの中で記述が食い違っているので両方覚えます。SQL 構文リファレンスが列挙しているのは CSV / JSON / AVRO / ORC / PARQUET / TEXT / BINARYFILE の 7 種類ですが、COPY INTO の概要ページは XML を含めた 8 種類を挙げ、XML のページには `FILEFORMAT = XML` の実例が載っています。XML は Databricks Runtime 14.3 以降で、通常は `FORMAT_OPTIONS ('rowTag' = '...')` を併用します。左辺がターゲット表、右辺がソース URI で、これを逆に書いた選択肢が誤答として並びます。

ターゲット表は先に無いと動かない

COPY INTO は既存の Delta 表しか受け付けません。存在しない表名を書いても表は自動作成されず、一時ビューにも外部表にもなりません。では取り込むデータの列構成が未確定のときはどうするか。ドキュメントが示す手順は、列を持たない空のプレースホルダ Delta 表を先に作り、mergeSchema を true にして推論させるというものです。スキーマを省略できるのは Databricks Runtime 11.3 LTS 以降で、スキーマ進化に対応した形式に限られます。

CREATE TABLE IF NOT EXISTS my_pipe_data;

COPY INTO my_pipe_data
FROM 's3://my-bucket/pipeData'
FILEFORMAT = CSV
FORMAT_OPTIONS ('mergeSchema' = 'true', 'delimiter' = '|', 'header' = 'true')
COPY_OPTIONS ('mergeSchema' = 'true');

この空表は COPY INTO 専用です。ドキュメントは、スキーマの無い Delta 表への書き込みに `INSERT INTO` と `MERGE INTO` はサポートされないと明記しています。データが入ってはじめて普通の表として問い合わせられます。権限面では、アカウント管理者がクラウドストレージへのアクセスを構成したうえで、実行するユーザーに対象のカタログ・スキーマ・ボリュームに対する CREATE TABLE 権限が必要です。

冪等性はどこに記録されているか

COPY INTO の最大の性質は再実行しても重複しないことです。仕組みは単純で、ソースの場所にあるファイルのうち既に読み込んだものは次回以降スキップされるだけです。記録はターゲット表側に置かれ、COPY INTO が作ったメタデータファイルは Databricks Runtime 15.2 以降なら `VACUUM` で掃除できます。ファイル内容のハッシュを毎回計算しているわけでも、内部で MERGE に切り替わっているわけでもありません。帰結が三つ出ます。

  • 同じ `COPY INTO` を二度実行すると、二度目は何も取り込まれず、例外も起きない。
  • ロード後にソース側でファイルの中身が書き換えられていても、取り込み済みとして扱われスキップされる。差分追記も行更新も起きず、チェックサム不一致で失敗することもない。
  • ファイル名を毎回変える運用にすると別ファイル扱いになり、冪等スキップが効かなくなる。

この冪等性を意図的に外すのが `COPY_OPTIONS ('force' = 'true')` です。既定は false で、true にすると取り込み済みかどうかにかかわらず全ファイルが読み込まれます。書き換え後の内容を反映したいときやバックフィルには必要ですが、本番ジョブに付けっぱなしにすると重複が積み上がります。

運用の側から冪等性を壊す例も覚えます。毎回 `DROP TABLE` と `CREATE TABLE` で作り直す、事前に `TRUNCATE TABLE` で空にする、ファイル名に毎回タイムスタンプを付ける。どれも記録を消すか鍵を変えるので全件再取り込みになります。冪等性はファイルのパスを鍵にして成立していると押さえてください。

同じ「二度取り込まない」でも、Auto Loader とは記録場所が違います。COPY INTO はターゲット表側、Auto Loader はチェックポイント内のキーバリューストア (RocksDB) です。記録が別々である以上、同じ表に両方を常用すると互いの履歴を参照せず二重取り込みの温床になります。ただしドキュメントは、Auto Loader のストリームを走らせたまま COPY INTO で一部のファイルだけ入れ直す使い方は認めています。

FORMAT_OPTIONS と COPY_OPTIONS を取り違えない

この振り分けは単独で一問になるほど頻出です。

誰に渡るか指定できるもの
`FORMAT_OPTIONS`FILEFORMAT に対応する Spark のデータソースリーダー`header` / `delimiter` / `multiLine` / `ignoreCorruptFiles` など、読み方に関するすべて
`COPY_OPTIONS`COPY INTO コマンド自身`force` (既定 false) と `mergeSchema` (既定 false) の 2 つだけ

COPY_OPTIONS に二つしか無いと知っていれば、`COPY_OPTIONS ('header' = 'true')` や `COPY_OPTIONS ('multiLine' = 'true')` を並べた選択肢は読んだ瞬間に消せます。「破損ファイルを飛ばして続行したい」は `FORMAT_OPTIONS ('ignoreCorruptFiles' = 'true')`、「複数行にまたがる整形済み JSON を読みたい」は `FORMAT_OPTIONS ('multiLine' = 'true')` です。CSV の `header` も JSON の `multiLine` も既定は false で、明示しないと先頭行がデータ行になります。

やっかいなのは `mergeSchema` が両方の句に書けることです。FORMAT_OPTIONS 側は複数ファイル間でスキーマを突き合わせる読み取り時の指定、COPY_OPTIONS 側はターゲット表のスキーマを進化させる指定です。列が増えたときに表を追随させたいなら COPY_OPTIONS 側。なお Auto Loader の救済列は COPY INTO では使えないので、取りこぼしたくないなら mergeSchema で表側を広げるしかありません。

VALIDATE — 書かずに確かめる

本番表へ書く前に確かめたいときに使うのが `VALIDATE` 句です。Databricks Runtime 10.4 LTS 以降で使え、付けるとデータは検証されるだけでテーブルには一切書き込まれません。検証されるのはパース可否、表のスキーマと一致するか (進化が必要か)、nullability と CHECK 制約を満たすか、の三点です。

  • `VALIDATE ALL`: 全データを検証する。VALIDATE を書いたときの既定。
  • `VALIDATE 100 ROWS`: 指定した行数を検証する。50 未満の数を指定すると 50 行以下のデータのプレビューが返る。

「書き込む前に確かめたい」に対して一時テーブルへ COPY INTO する案は、実際に書き込みが起きるので不正解です。

BY POSITION — 列名が合わないとき

ソースの列名がターゲット表と一致しないが並び順は一致している。この状況を解決するのが `BY POSITION` で、ソース列とターゲット列を序数位置で対応付け、型のキャストも自動で行います。使えるのはヘッダーなし CSV だけで、`FILEFORMAT = CSV` の指定と `FORMAT_OPTIONS ("headers" = "false")` が必要です (これが既定値でもあります)。

COPY INTO bronze.sales BY POSITION
FROM 's3://my-bucket/headerless/'
FILEFORMAT = CSV;

表記に注意します。BY POSITION で書くのは複数形の `headers` で、CSV リーダーの `header` とは別のキーです。誤答肢に並ぶ `FORMAT_OPTIONS ('header' = 'false')` は先頭行をデータとして読む指定でしかなく、列名の食い違いは解決しません。対応付けではターゲット表の IDENTITY 列 (連番が自動採番される列) と GENERATED 列 (式から自動計算される列) が無視され、残った列数とソース列数が合わなければエラーになります。

FILES と PATTERN — 対象を絞る二つの句

書き方制約
`FILES`ロードするファイル名を列挙する指定できるのは 1000 件まで
`PATTERN`グロブパターン (ワイルドカードでファイル名をまとめて指定する書き方) でソースディレクトリ内を絞る`?` `*` `[abc]` `[a-z]` `[^a]` `{ab,cd}` が使える

この二つは同時に指定できません。FILES で明示したファイルでも冪等性の判定は生きていて、取り込み済みなら黙ってスキップされます。PATTERN は読み込む対象を選ぶ句であり、除外を書く句でも履歴管理を無効化する句でもありません。

WITH ( CREDENTIAL ... ) とロード時の変換

ソースパスへのアクセス資格情報は、FROM 句のパスに続けて `WITH` で渡します。`COPY_OPTIONS` や `FORMAT_OPTIONS` に `credential` というオプションは存在しません。名前付き資格情報のほかに一時資格情報 (AWS なら `AWS_ACCESS_KEY` / `AWS_SECRET_KEY` / `AWS_SESSION_TOKEN`、Azure なら `AZURE_SAS_TOKEN`) も渡せ、同じ括弧に `ENCRYPTION` も並べられます。

COPY INTO bronze.sales
FROM 's3://my-bucket/raw/' WITH ( CREDENTIAL my_named_credential )
FILEFORMAT = PARQUET;

ロードしながら型を変えたり列を落としたりしたい場合は、FROM 句にサブクエリを書きます。オプション句は `key = value` しか受け取らないので、式を書ける場所はここだけです。

COPY INTO my_delta_table
FROM (SELECT to_date(dt) dt, event as measurement, quantity::double
      FROM 's3://my-bucket/avroData')
FILEFORMAT = AVRO;

同時実行と選定基準

同じ表に対する複数の COPY INTO は、それぞれが扱う入力ファイルの集合が互いに素 (共通のファイルを一つも含まない状態) であれば、いずれも最終的に成功します。重なるとトランザクション競合になります。ただし 1 つのコマンドに複数ファイルを渡す方が速いため、性能目的の並行実行は推奨されていません。

選定の目安は明快です。時間をかけて取り込むファイルが数千のオーダーなら COPY INTO、数百万以上が見込まれるなら Auto Loader。スキーマが頻繁に変わる場合も Auto Loader が向きます。SQL だけでより拡張性の高い形を求めるなら、ドキュメントが示す代替は `CREATE STREAMING TABLE` と `read_files` によるストリーミングテーブルです。

三面比較 — 同じ「新しい列」に三者はどう答えるか

観点Delta への書き込みAuto LoaderCOPY INTO
既定の反応AnalysisException で拒否`UnknownFieldException` で停止し再起動で追加新列は表に取り込まれない
進化を許す指定`mergeSchema` オプション`cloudFiles.schemaEvolutionMode``COPY_OPTIONS ('mergeSchema' = 'true')`
捨てずに退避する仕組みなし`_rescued_data` 列なし
重複防止の鍵書き込み側の実装しだいチェックポイント内の RocksDBターゲット表側のロード済みファイル記録
実行形態バッチ / ストリーミングStructured Streaming のソースSQL のバッチコマンド

並べると、救済列を持つのは Auto Loader だけだと分かります。優劣ではなく、必要なのが救済かチェックポイント不要かで選びます。

確認 — 穴あけ 3

0 / 3

空欄を押すと選択肢が出ます。間違えても減点はありません。

COPY INTO の COPY_OPTIONS に指定できるのは の 2 つだけです。

取り込み済みのファイルがソース側で書き換えられた場合、次回の COPY INTO では

COPY INTO の FILES 句で指定できるファイル名は までで、PATTERN 句とは同時に指定できません。

この章のまとめ

  1. Delta の既定はスキーマ強制で、列追加は mergeSchema か DDL で明示的に許可する
  2. Auto Loader の推論は 50 GB か 1000 ファイルのサンプリングで打ち切られる
  3. schemaEvolutionMode の既定は未指定なら addNewColumns、明示なら none
  4. rescuedDataColumn は未知列・型不一致・大小文字違いを JSON で退避する
  5. COPY_OPTIONS は force と mergeSchema の 2 つだけ、FILES は 1000 件上限

この端末にだけ保存されます(登録不要)

この章の根拠

最終確認 2026-08-09 / 対応バージョン DEA 2026-05-04 改訂版

コース全体

  1. Databricks とレイクハウスの全体像 — なぜ必要か、何がどこに属し、どの計算資源で動くか
  2. データはどこに、どんな形で保存されるか — テーブルの実体と Delta Lake の内部
  3. 計算資源を作り込み、コードを書く場所を決める — クラスタ設定とノートブック/ローカル IDE
  4. Spark はどう動き、SQL で何をどこまで書けるか
  5. PySpark でデータを加工し、遅いコードを見抜く
  6. 流れ込むデータを受け止める — Structured Streaming と Auto Loader のファイル検出
  7. 取り込み時にスキーマをどう扱うか — Auto Loader のスキーマ進化と COPY INTO
  8. ファイル以外からも取り込む — read_files・Lakeflow Connect・JDBC と API
  9. 生データを使える形に育てる — メダリオン設計と宣言的パイプライン
  10. 処理を1つのジョブに束ねる — タスク種別と依存関係の設計
  11. ジョブを動かし、失敗から立て直す — トリガー・リトライ・パラメータ
  12. 書いたものを安全に本番へ届ける — Git 連携とデプロイの自動化
  13. 誰に何を見せるか — Unity Catalog のアクセス制御とアイデンティティ統制
  14. 誰が何をしたか、いくらかかったか、どこで詰まったかを見る
  15. テーブルを保守して速くする — 運用コマンド・保持期間・レイアウト設計・障害復旧
  16. DEA 直前仕上げ — 方式選定・出題範囲対応表・数値総まとめ・引っかけの型