【BigQuery】GCS上のCSVやParquetを直接クエリする外部テーブル作成手順

Google Cloud Storage(GCS)に保存されたデータをBigQueryで分析する際、通常は「データをBigQueryのネイティブテーブルにロード」して使用します。 しかし、「ETL処理前のデータをちょっとプレビューしたい」「日次で吐き出される大量のログファイルをいちいちロードしたくない」というケースもあるでしょう。

そんな時に便利なのが、BigQueryの外部テーブル(External Table)機能です。

本記事では、GCS上のCSVやParquetファイルをBigQueryにロードすることなく、直接SQLでクエリできる「外部テーブル」の作成手順を解説します。複数ファイルの結合や、スキャンコストを削減するHiveパーティショニングの設定方法、さらには利用時の制限事項まで網羅しました。

1. BigQueryの「外部テーブル」とは?

外部テーブルとは、データの実体はBigQueryのストレージではなくGCSなどの外部ストレージに置いたまま、BigQueryからSQLで検索できる仮想的なテーブルのことです。

ネイティブテーブル(データをBigQuery内にロードしたテーブル)と比較すると、以下のような特徴があります。

項目外部テーブルネイティブテーブル
データの場所GCSなどの外部ストレージBigQueryマネージドストレージ
ロードの手間不要(即座にクエリ可能)必要(ジョブ実行など)
クエリの速度ネイティブに比べて遅い高速(ストレージの最適化が効く)
DML(更新・削除)不可(読み取り専用)可能
コストGCSの保存費用 + BQのクエリスキャン費用BQの保存費用 + BQのクエリスキャン費用

外部テーブルが適しているケース

頻繁に更新されるログファイルの集計や、ETLパイプラインを構築する前のデータ探索(プレビュー)、たまにしか検索しない過去データのアーカイブ用途などに最適です。

2. 外部テーブルの作成手順(SQL構文)

外部テーブルは、Google Cloudコンソールの画面上からも作成可能ですが、再現性や自動化の観点から CREATE EXTERNAL TABLE 構文を使用したSQLでの作成がおすすめです。

CSVファイルを読み込む場合

CSVファイルの場合は、カンマ区切りであることやヘッダー行のスキップなどをオプションで指定します。

CREATE OR REPLACE EXTERNAL TABLE `my_project.my_dataset.my_external_csv`
OPTIONS (
  format = 'CSV',
  uris = ['gs://my-bucket/data/sample.csv'],
  skip_leading_rows = 1 -- ヘッダー行をスキップ
);

Parquetファイルを読み込む場合

Parquet(列指向フォーマット)は、スキーマ情報がファイル内に含まれているため、よりシンプルに定義できます。また、BigQueryとの相性も良く、CSVよりスキャン効率やパフォーマンスが高くなります。

CREATE OR REPLACE EXTERNAL TABLE `my_project.my_dataset.my_external_parquet`
OPTIONS (
  format = 'PARQUET',
  uris = ['gs://my-bucket/data/sample.parquet']
);

3. 複数ファイルの一括読み込み(ワイルドカードURI)

特定のディレクトリ以下にある複数のファイル(日別のログファイルなど)をまとめて1つのテーブルとして扱いたい場合は、uris オプションにワイルドカード(*)を指定します。

CREATE OR REPLACE EXTERNAL TABLE `my_project.my_dataset.my_daily_logs`
OPTIONS (
  format = 'CSV',
  uris = ['gs://my-bucket/logs/2026/*.csv'], -- 2026年配下の全CSVを対象
  skip_leading_rows = 1
);

このように指定することで、gs://my-bucket/logs/2026/ 配下に新しくCSVファイルが追加されても、自動的にクエリの対象に含まれるようになります。

4. Hiveパーティショニングによるスキャン削減法

外部テーブルの最大の弱点は、クエリ実行時に「対象となる全ファイルを都度フルスキャンしてしまう」ことです。データ量が増えるとクエリコストが増大し、パフォーマンスも低下します。

これを解決するのが、Hiveパーティショニングです。 GCS上のファイル配置を gs://my-bucket/logs/year=2026/month=10/ のような キー=値 のディレクトリ構造にしておくことで、BigQuery側で不要なディレクトリの読み込みをスキップ(プルーニング)させることができます。

Hiveパーティションを有効にしたテーブル作成

CREATE OR REPLACE EXTERNAL TABLE `my_project.my_dataset.partitioned_logs`
(
  -- ファイル内のカラム定義
  id STRING,
  message STRING,
  -- パーティションキーの定義
  year INT64,
  month INT64
)
WITH PARTITION COLUMNS (
  year INT64,
  month INT64
)
OPTIONS (
  format = 'PARQUET',
  uris = ['gs://my-bucket/logs/*'],
  hive_partition_uri_prefix = 'gs://my-bucket/logs/' -- 基準となるパス
);

上記のように定義したテーブルに対して、以下のようなクエリを実行します。

SELECT *
FROM `my_project.my_dataset.partitioned_logs`
WHERE year = 2026 AND month = 10;

すると、BigQueryは year=2026/month=10/ のディレクトリにあるファイルだけをスキャンするため、大幅なコスト削減と高速化が実現します。

【注意】パーティションキーとカラム名の重複

Hiveパーティショニングを利用する場合、ファイル内のカラム名とパーティションキー(year や month など)が重複しないように注意してください。

5. 制限事項とネイティブテーブルへの移行タイミング

便利な外部テーブルですが、利用する際には以下の制限事項を理解しておく必要があります。

  • 読み取り専用: INSERT, UPDATE, DELETE, MERGE などのDML文は実行できません。データを変更したい場合は、GCS上のファイルを直接置き換える必要があります。
  • 機能制限: テーブルのコピー(Copy Job)やクラスタリング機能はサポートされていません。
  • パフォーマンスの限界: Hiveパーティションを使っても、最終的には外部ストレージからのフェッチになるため、ネイティブテーブルの速度には及びません。また、クエリのキャッシュが効きにくい点にも注意が必要です。

ネイティブテーブルへの切り替えタイミングは?

以下のような要件が出てきた場合は、外部テーブルからネイティブテーブルへデータを取り込む(ロードする)構成への切り替えを推奨します。

  1. DASHBOARDやBIツールと連携する場合: 頻繁にクエリが実行されるため、ネイティブテーブルのキャッシュと高速なレスポンスが必須になります。
  2. データ加工(DML)が必要な場合: 抽出したデータを元に新しいテーブルを生成・更新する処理が必要な場合。
  3. より高度なアクセス制御が必要な場合: (※近年では「BigLakeテーブル」を利用することで、外部データでも細かいアクセス制御が可能になっていますが、基本的にはネイティブの方が管理が容易です)。

まとめ

BigQueryの「外部テーブル」は、GCS上のデータを手軽にクエリできる非常に強力な機能です。

  • ETL不要で即座にデータプレビューや分析が可能
  • ワイルドカード指定で複数ファイルを簡単に結合
  • Hiveパーティショニング(year=.../month=...)でスキャン量とコストを削減

これらの特性と制限事項を正しく理解し、ネイティブテーブルと上手く使い分けることで、より効率的なデータ分析基盤を構築していきましょう。