第3章 データロード・アンロードと接続(Loading, Unloading & Connectivity, 18%)
🎯 この節の学習目標
ステージに置いたファイルをテーブルにロードする中心的なコマンドが COPY INTO <table> です。これはバルクロード(bulk load)と呼ばれる、まとまったデータを一括で取り込む方式です。
-- 最も基本的な形:ステージのファイルをテーブルへロードする
COPY INTO sales
FROM @my_stage
FILE_FORMAT = (FORMAT_NAME = 'my_csv_format');
-- 対象ファイルを絞り込む2つの方法
COPY INTO sales FROM @my_stage
FILES = ('sales_01.csv.gz', 'sales_02.csv.gz'); -- ファイル名を列挙
COPY INTO sales FROM @my_stage
PATTERN = '.*sales_2026.*[.]csv[.]gz'; -- 正規表現で指定
COPY INTO の実行には、実行中の仮想ウェアハウスが必要です(ここが 3-3 のサーバーレスな Snowpipe との大きな違いです)。ウェアハウスのサイズは「大きいほど速い」と単純には言えず、並列処理できるのはファイル数までなので、ファイル数に応じたサイズを選ぶのが基本です。ファイルが1つしかなければ、ウェアハウスを大きくしてもそのファイルのロードは速くなりません。
図:COPY INTO によるバルクロードの流れ。検証・重複防止・エラー処理の各ポイントを押さえる
COPY INTO は、どのファイルをいつロードしたかというメタデータをテーブルごとに 64日間保持します。このおかげで、同じ COPY 文を誤って再実行しても、ロード済みのファイルは自動的にスキップされ、データが二重に入ることを防げます。
FORCE = TRUE を指定します。この場合は重複チェックを行わずロードするため、データが重複する可能性を理解した上で使います。📝 試験のポイント
「COPY INTO のロードメタデータは64日」「Snowpipe のロード履歴は14日」という日数の対比は、試験で定番の論点です(3-3 で再度整理します)。また「同じファイルを強制的に再ロードするオプションは何か」と問われたら FORCE = TRUE と即答できるようにしましょう。
ロード中に形式不正な行などのエラーに遭遇したとき、どう振る舞うかを決めるのが ON_ERROR オプションです。
| 設定値 | 動作 | 向いている場面 |
|---|---|---|
| ABORT_STATEMENT | エラーを検出した時点でロード全体を中止する。COPY INTO の既定値 | 不正データを1行も入れたくない、原因を調べてからやり直したい場合 |
| CONTINUE | エラー行だけをスキップして、残りの行のロードを続行する | 多少の不正行より、ロードを止めないことを優先する場合 |
| SKIP_FILE | エラーを含むファイル単位で丸ごとスキップし、他のファイルは続行する | ファイル単位で完全性を保ちたい場合 |
| SKIP_FILE_n / SKIP_FILE_n% | エラーが n 件(または n%)以上になったファイルだけをスキップする | 少数のエラーは許容し、壊れ方がひどいファイルだけ除外したい場合 |
本番のロードを実行する前に「このファイルはエラーなくロードできるか」を確かめたいことがあります。VALIDATION_MODE を指定した COPY INTO は、データを一切ロードせずに検証だけを行います。
-- ファイルの先頭10行を解析し、変換結果を表示する(ロードはしない)
COPY INTO sales FROM @my_stage
VALIDATION_MODE = 'RETURN_10_ROWS';
-- すべてのファイルを検証し、見つかったエラーを一覧で返す(ロードはしない)
COPY INTO sales FROM @my_stage
VALIDATION_MODE = 'RETURN_ERRORS';
-- 過去に実行した COPY のエラーを後から確認する VALIDATE 関数
SELECT * FROM TABLE(VALIDATE(sales, JOB_ID => '_last'));
COPY INTO は、FROM 句に SELECT を書くことで、ロードと同時に簡単な変換を行えます。ただし、できる変換は限定的です。
| できること | できないこと |
|---|---|
| 列の並べ替え・省略、CAST による型変換、SUBSTR などの単純な関数適用、定数列の追加 | 結合(JOIN)、集約(GROUP BY)、FLATTEN のような複雑な変換(必要ならいったんロードしてから変換する) |
💡 具体例:ロード時の列選択と変換
-- ステージファイルの1列目と3列目だけを、型変換しながらロードする
COPY INTO sales (sale_id, amount)
FROM (
SELECT t.$1, CAST(t.$3 AS NUMBER(10,2))
FROM @my_stage t
)
FILE_FORMAT = (FORMAT_NAME = 'my_csv_format');
-- 列名でマッピングする(Parquet などの列名付き形式で便利)
COPY INTO sales FROM @my_stage
FILE_FORMAT = (TYPE = 'PARQUET')
MATCH_BY_COLUMN_NAME = CASE_INSENSITIVE;
-- ロード成功後にステージのファイルを自動削除する
COPY INTO sales FROM @my_stage PURGE = TRUE;
$1, $2, ... はファイル内の列位置を指します。MATCH_BY_COLUMN_NAME は、ファイル内の列名とテーブルの列名を突き合わせてロードするオプションです。PURGE = TRUE はロード成功後にステージからファイルを削除し、後片付けを自動化します。
「どのファイルがいつ、何行ロードされ、エラーは何件だったか」は、次の手段で確認できます。
| 手段 | 場所 | 特徴 |
|---|---|---|
| COPY_HISTORY(テーブル関数) | INFORMATION_SCHEMA | 指定テーブルへのロード履歴を確認。COPY と Snowpipe の両方の履歴を含む |
| LOAD_HISTORY(ビュー) | INFORMATION_SCHEMA / ACCOUNT_USAGE | COPY INTO によるロード履歴。INFORMATION_SCHEMA 版には Snowpipe の履歴は含まれない |
-- 直近のロード状況をテーブル関数で確認する
SELECT file_name, row_count, error_count, last_load_time
FROM TABLE(INFORMATION_SCHEMA.COPY_HISTORY(
TABLE_NAME => 'SALES',
START_TIME => DATEADD(hour, -24, CURRENT_TIMESTAMP())
));
✅ この節のまとめ
問1. 先週ロードしたファイルと同じファイルに対して COPY INTO を再実行した。既定の動作として正しいものはどれか。
正解:B
COPY INTO はテーブルごとに64日間のロードメタデータを保持しており、ロード済みと判定されたファイルは自動的にスキップされます。Aが起きるのは FORCE = TRUE を指定した場合や、64日を超えてメタデータが期限切れになった場合です。Cのようなエラーにはならず、スキップされるだけです。Dのような既存データの削除(TRUNCATE 相当)を COPY が勝手に行うことはありません。
問2. ON_ERROR オプションを何も指定せずに COPY INTO を実行した場合の、エラー発生時の既定動作はどれか。
正解:C
COPY INTO の ON_ERROR の既定値は ABORT_STATEMENT で、エラーを検出するとロード全体が中止されます。AとBは明示的に指定した場合の動作です。Dのような自動修正機能はありません。なお、Snowpipe では既定値が異なり SKIP_FILE である点が 3-3 で登場します。
問3. 本番ロードの前に、ステージ上の全ファイルをロードせずに検証し、含まれるエラーを一覧で確認したい。最も適切な方法はどれか。
正解:A
VALIDATION_MODE = 'RETURN_ERRORS' は、データをロードせずに対象ファイルを検証し、すべてのエラーを返します。Bはロード済みファイルの強制再ロードで、検証とは無関係です。Cは実際にロードを行いながらエラー行をスキップする設定なので、「ロードせずに」という要件を満たしません。Dはロード成功後にステージのファイルを削除するオプションです。
問4. COPY INTO のロード時変換(FROM 句での SELECT)でできる操作はどれか。
正解:C
COPY INTO のロード時変換でサポートされるのは、列の並べ替え・省略、CAST、SUBSTR のような単純な関数適用など、行単位で完結する軽い変換に限られます。A の結合、B の集約、D のウィンドウ関数はサポートされません。複雑な変換が必要な場合は、いったんテーブルにロードしてから SQL(INSERT ... SELECT や Streams / Tasks、Dynamic Tables)で変換します。