BigQuery ハンズオン教科書|2026-08 版
第 3 章

データ取り込み — バッチロードと転送

CSV・JSON・Parquet を無料のロードジョブで取り込み、エクスポートまで往復する

この章で学ぶこと

前提: ハンズオン 3-1〜3-3 はサンドボックスで完走可(無料枠内)。ハンズオン 3-4(エクスポート)のみ Cloud Storage バケットの作成が必要なため課金プロジェクト必須(概算 1 円未満)。

取り込み方法の全体地図

BigQuery へデータを入れる方法は 1 つではありません。データの発生源・鮮度要件・運用したい人手のかけ方によって、選ぶべき経路が変わります。公式ドキュメントは取り込み方法を大きく「バッチロード」「ストリーミング」「BigQuery Data Transfer Service による定期転送」「外部データソースへのフェデレーション」に整理しています。

方法主な用途この章での扱い
バッチロード大量データを定期的・一括で取り込むこの章の主役。bq load と LOAD DATA 文
ストリーミングメッセージングシステムから秒〜分単位で近リアルタイム取り込み第 12 章で詳説。この章では扱わない
BigQuery Data Transfer ServiceGoogle 広告・YouTube・SaaS 等からのスケジュール実行の自動化位置づけのみ。サンドボックス非対応
外部データソース・フェデレーションロードせずに外部の場所へ直接クエリする第 10 章で詳説
ローカル / GCS ファイル イベント ソース SaaS / 広告データ Cloud SQL / Spanner 等 BigQuery データセット Cloud Storage バッチロード ストリーミング 転送(DTS) フェデレーション エクスポート
図 3-1 BigQuery への 4 つの入力経路と、Cloud Storage への出力(エクスポート)経路。この章はバッチロードとエクスポートを扱う。
実務の視点: 取り込み頻度が低くまとめて処理してよいならバッチロード、秒〜分単位の鮮度が必要ならストリーミング(第 12 章)を選びます。Google 広告や SaaS からの定期取得は BigQuery Data Transfer Service に任せると運用の手間が減ります。ロードせず参照するだけでよいデータは外部テーブル(第 10 章)が候補になります。

ロードジョブの費用・ロケーション・フォーマット

バッチロードの実体は「ロードジョブ」です。公式ドキュメントには「共有スロットプール(shared slot pool)を使う限り、BigQuery へのバッチロードに料金は発生しない」と明記されています(2026-08 時点)。ロード自体は無料ですが、ロード先データセットのロケーションとロード元(Cloud Storage バケット等)のロケーションが一致している必要があり(第 2 章)、ロケーションをまたぐ場合はデータ転送料金が発生することがあります。

ロード可能な形式は Avro・CSV・JSON(改行区切り)・ORC・Parquet です。形式によって並列読み込みの可否が変わり、ロード時間に直結します。

形式並列読み込み特徴
CSV / JSON非圧縮なら可、圧縮(.gz)は不可人間が読みやすく自動検出も効くが、圧縮するとロードが遅くなる
Avro圧縮していても可自己記述型でスキーマ不要。ロードが最速の部類
Parquet可(列指向)列単位の効率的なエンコーディング。分析ワークロードの定番
ORC可(ストライプ単位)ロード時間の最適化には 1 ストライプ約 256 MB 以下が目安
圧縮 CSV / JSON 単一ワーカー 逐次読み込み テーブル Avro / Parquet / ORC ワーカー A ワーカー B テーブル 読み込み 書き込み 並列読み込み マージ
図 3-2 圧縮 CSV / JSON は単一ストリームで逐次処理されるが、Avro / Parquet / ORC は複数ワーカーに分割して並列に読み込める。
実務の視点: 取り込み速度とコストを両立させたいなら Avro か Parquet を選びます。手元のデータが CSV しかない場合、圧縮せずにアップロードした方が速くロードできます(圧縮 CSV は並列読み込みできないため)。

ローカルファイルと Cloud Storage、どちらからロードするか

バッチロードのソースには、ローカルマシンと Cloud Storage の両方を指定できますが、性質はかなり異なります。

ローカルファイル (コンソールは 100 MB) Cloud Storage ファイル ロードジョブ (無料・共有スロット) BigQuery テーブル bq load LOAD DATA 無料
図 3-3 ローカルファイルは bq load のみ、Cloud Storage は bq load と LOAD DATA 文の両方が使える。どちらも同じ無料のロードジョブに合流する。

スキーマ自動検出と明示スキーマ

CSV と JSON はスキーマを自己記述しないため、BigQuery はスキーマ自動検出(--autodetect)を提供しています。仕組みはシンプルで、データソースからランダムに 1 ファイルを選び、先頭から最大 500 行をサンプルとして型を推定します。CSV では区切り文字とヘッダー行の有無も推定し、JSON ではネストしたオブジェクトを RECORD 型、配列を繰り返しフィールドとして扱います。

ただし限界もあります。公式ドキュメントには次のような注意点が明記されています。

スキーマの精密な制御が必要な場面、非標準の日時形式を扱う場面、そして全列 STRING 型の CSV を扱う場面では、明示スキーマを使うべきです。

書き込みモードとパーティションへのロード

ロードジョブには 3 つの書き込みモードがあります。

LOAD DATA 文ではこれを INTO(追記。テーブルが無ければ新規作成)と OVERWRITE(置換)というキーワードで表現します。パーティション表に対しては、PARTITION BY 句を付けて新規作成時にパーティション定義を与えるほか、PARTITIONS (partition_column_name=partition_value) 句を使うと特定の 1 パーティションだけを追記・置換できます(このときは LOAD DATA INTO で始める必要があります)。

ロード実行 WRITE_EMPTY 空なら書込み/既存はエラー WRITE_APPEND (既定) 末尾に追記 WRITE_TRUNCATE 全データを置換 EMPTY APPEND TRUNCATE
図 3-4 同じロード実行でも、書き込みモードによってテーブルの結果状態が 3 通りに分岐する。

転送サービスとエクスポート — 逆方向のデータ移動

BigQuery Data Transfer Service は、Google 広告・YouTube・Cloud Storage・Amazon S3・Salesforce といった多様なソースから BigQuery へのデータ移動を、スケジュールに基づいて自動化するサービスです。サービス自体に料金はかからず、転送後のデータに対する通常のストレージ・クエリ課金だけが発生します。ただし BigQuery サンドボックスでは利用できないため、本書ではこの位置づけの説明にとどめ、実際の設定はコンソールの「Pipelines & Connections」から行うという流れだけ押さえておきます。

逆方向、つまりテーブルから Cloud Storage への書き出しには bq extract コマンドと EXPORT DATA 文を使います(具体的なコマンドはハンズオン 3-4 で実行します)。CSV・JSON・Avro・Parquet 形式に対応し、1 ファイルにつき論理データサイズ 1 GB までという制限があるため、超える場合はワイルドカードで複数ファイルに分割します。EXPORT DATA 文なら SQL の中で完結させられます。

EXPORT DATA
  OPTIONS (
    uri = 'gs://PROJECT_ID-handson-ch03/us_states/*.csv',
    format = 'CSV',
    overwrite = true,
    header = true)
AS (
  SELECT state_name, state_abbr
  FROM handson_ch03.us_states
);
制限項目値(2026-08 時点)
1 ファイルの論理データサイズ上限1 GB(超える場合はワイルドカードで分割)
1 エクスポートジョブあたりのワイルドカード URI最大 500
1 プロジェクトあたりのエクスポートジョブ数1 日 100,000 件
Cloud Storage (GCS バケット) BigQuery テーブル ロードジョブ (無料) エクスポートジョブ (GCS 書込み課金)
図 3-5 ロードとエクスポートは同じ 2 点間を逆方向に移動するが、エクスポート側だけ Cloud Storage への書き込み課金が発生する。
ハンズオン 3-1|ローカル CSV を自動検出でロードする

公開データセット cloud-samples-data の米国州名 CSV をローカルに保存し、自動検出でロードします。ファイルは約 700 バイトなのでサンドボックスで無料・数秒で完走します。

  1. US ロケーションでデータセットを作成し、米国州名 CSV をローカルにダウンロードします(name,post_abbr の 2 列 STRING、ヘッダー 1 行 + データ 50 行)。
bq mk --dataset --location=US PROJECT_ID:handson_ch03

curl -o us-states.csv \
  https://storage.googleapis.com/cloud-samples-data/bigquery/us-states/us-states.csv
  1. 自動検出でロードします。
bq load \
  --autodetect \
  --source_format=CSV \
  --location=US \
  handson_ch03.us_states \
  ./us-states.csv
  1. 行数を確認します。
SELECT COUNT(*) AS row_count FROM `PROJECT_ID.handson_ch03.us_states`;

期待される出力は row_count = 50 です。ただし自動検出の仕組み上、この CSV のように 2 列とも STRING 型の場合はヘッダー行がデータ扱いされ 51 になることがあります(次のハンズオンで明示スキーマによる確実な方法を試します)。

後片付け: このテーブルは次のハンズオンで作り直すため、いったんそのままで構いません。

ハンズオン 3-2|明示スキーマ + LOAD DATA 文で書き込みモードを検証する

同じ CSV を、今度は Cloud Storage の公開バケットから直接、明示スキーマで確実にロードします。公開データを読むだけなので追加の課金プロジェクトは不要です(処理量は 1 KB 未満)。

  1. まずテーブルを作り直し、明示スキーマと skip_leading_rows でヘッダーを確実に除外します。
LOAD DATA OVERWRITE handson_ch03.us_states
(state_name STRING, state_abbr STRING)
FROM FILES (
  format = 'CSV',
  skip_leading_rows = 1,
  uris = ['gs://cloud-samples-data/bigquery/us-states/us-states.csv']
);
  1. SELECT COUNT(*) で 50 になっていることを確認します。
  2. 既定の書き込みモード(追記)を確認するため、先頭の LOAD DATA OVERWRITE を LOAD DATA INTO に変えるだけで同じ文をもう一度実行します。

行数は 100 になります(重複を許して追記された状態)。OVERWRITE で再実行すれば 50 に戻ります。

後片付け: このテーブルはハンズオン 3-4 で使うため、最後に OVERWRITE を実行して 50 行の状態に戻しておきます。

ハンズオン 3-3|タイムスタンプ列でパーティション表を作りながらロードする

公開データセットの取引サンプル(6 行、1 KB 未満)を、LOAD DATA 文 1 本でパーティション表として新規作成しながらロードします。サンドボックスで無料・数秒で完走します。

  1. パーティション定義付きでロードします。
LOAD DATA INTO handson_ch03.transactions
(timestamp TIMESTAMP, origin STRING, destination STRING, transaction_amount NUMERIC)
PARTITION BY TIMESTAMP_TRUNC(timestamp, DAY)
FROM FILES (
  format = 'CSV',
  skip_leading_rows = 1,
  uris = ['gs://cloud-samples-data/bigquery/sample-transactions/transactions.csv']
);
  1. 作られたパーティションを確認します。
SELECT partition_id, total_rows
FROM `PROJECT_ID.handson_ch03.INFORMATION_SCHEMA.PARTITIONS`
WHERE table_name = 'transactions'
ORDER BY partition_id;

6 行のデータ(2017-03-18 は同日 2 件)が 5 つの異なる日付にまたがっているため、パーティションは 5 個作成されます。

後片付け:

bq rm -f -t PROJECT_ID:handson_ch03.transactions
ハンズオン 3-4|Cloud Storage へエクスポートして往復を完結させる(課金プロジェクト必須・概算 1 円未満)

ハンズオン 3-2 で作った 50 行のテーブルを、自分の Cloud Storage バケットへ書き出します。バケットの新規作成には課金が有効なプロジェクトが必要です。50 行・1 KB 未満のデータなので Cloud Storage の保存料金は月あたり実質ゼロに近い金額(US マルチリージョン Standard storage は 1 GB あたり月額 0.026 米ドル、2026-08 時点)です。

  1. データセットと同じ US ロケーションにバケットを作成します。
gcloud storage buckets create gs://PROJECT_ID-handson-ch03 --location=US
  1. エクスポートします。
bq extract \
  --destination_format=CSV \
  handson_ch03.us_states \
  gs://PROJECT_ID-handson-ch03/us_states/*.csv
  1. 書き出されたファイルを確認します。
gcloud storage ls gs://PROJECT_ID-handson-ch03/us_states/

1 ファイルに収まるサイズなら us_states/000000000000.csv のような 1 ファイルが、複数に分割された場合は連番のファイルが確認できます。

後片付け:

gcloud storage rm -r gs://PROJECT_ID-handson-ch03
bq rm -r -f -d PROJECT_ID:handson_ch03

つまずきポイント

全列 STRING の CSV でヘッダーが混入する: 自動検出は「1 行目だけが文字列で、他の行に別の型がある」という基準でヘッダー行を判定します。すべての列が STRING 型の CSV(州名や都市名のようなマスタデータに多い)では、この基準が働かずヘッダー行がデータとして取り込まれることがあります。ロード後は必ず行数を数える習慣をつけ、疑わしければ明示スキーマ + skip_leading_rows に切り替えます。
LOAD DATA 文にローカルパスを渡すとエラーになる: LOAD DATA 文の FROM FILES の uris は gs://・s3://・azure:// といったクラウド URI のみを受け付け、ローカルファイルパスは指定できません(なお s3:// / azure:// は BigQuery Omni の接続を WITH CONNECTION で指定する前提です)。ローカルファイルをロードしたい場合は bq load コマンドを使います。
ロケーション不一致でロードジョブが失敗する: データセットのロケーションと Cloud Storage バケットのロケーションが一致していないと、ロードジョブはエラーになります(第 2 章)。bq load には --location を明示し、バケット作成時のロケーションと揃えます。
圧縮ファイルのサイズ上限と課金の見落とし: 圧縮 CSV / JSON は並列読み込みできないうえ、圧縮ファイルは 4 GB までという上限があります(非圧縮なら 5 TB)。大きなデータは非圧縮にするか Avro / Parquet へ変換します。また、エクスポート用に作成した Cloud Storage バケットは BigQuery の無料枠の対象外なので、使い終えたら忘れずに削除します。

理解度チェック

Q1. バッチロードとストリーミングは、それぞれどんな場合に選ぶべきか?

データを定期的・一括で取り込んでよく、秒〜分単位の鮮度が不要ならバッチロードを選びます。メッセージングシステムから近リアルタイムで反映したい場合はストリーミング(第 12 章の Storage Write API 等)を選びます。

Q2. ロード時に Avro や Parquet が推奨されるのはなぜか?

どちらも自己記述型でスキーマ推定が不要なうえ、圧縮していても複数ワーカーで並列に読み込めるためです。CSV / JSON は圧縮すると単一ストリームでの逐次読み込みになり、ロードが遅くなります。

Q3. スキーマ自動検出が失敗しやすいのはどんなケースか?

全列が空でデフォルトが STRING 型に倒れるケース、全列が STRING 型でヘッダー行を判定できないケース、そして一部の非標準なタイムスタンプ表記が認識されないケースです。これらの場面では明示スキーマを使います。

Q4.(シナリオ)全列が STRING 型の日次 CSV ファイルを、毎日既存テーブルへ追記していきたい。最も適切な方法は?
A. bq load --autodetect のみを毎日実行する B. 明示スキーマ + skip_leading_rows=1 を指定し、既定の書き込みモード(追記)で bq load する C. 毎回 LOAD DATA OVERWRITE で全置換する D. Cloud Storage を使わずローカルファイルだけで運用し続ける

B。全列 STRING の CSV は自動検出でヘッダーを誤認識する恐れがあるため A は不適切です。C は「追記していきたい」という要件に反し、過去データを毎回消してしまいます。D はロード自体は可能でも、ローカルロードはワイルドカードが使えず 1 ファイルずつの逐次処理になるため、日次の継続運用には Cloud Storage 経由が向いています。

Q5.(シナリオ)ローカル PC に 50 MB の CSV があり、Cloud Storage バケットを作る予算はまだ承認されていない。BigQuery サンドボックスへロードするには?
A. LOAD DATA 文でローカルパスを直接指定する B. まず Cloud Storage バケットを作ってからロードする C. bq load コマンドでローカルファイルを直接指定してロードする D. サンドボックスではロードそのものができないので諦める

C。LOAD DATA 文はローカルパスを受け付けないため A は誤りです。B は課金プロジェクトが前提になり要件に反します。ローカルファイルのロードはサンドボックスの非対応機能一覧に含まれないため D も誤りです。

まとめと次章へ

データが正しく入ったので、次章からはいよいよ GoogleSQL でそのデータに問い合わせていきます。