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

総合演習 — ミニ分析基盤をゼロから組む

取り込み → 変換 → 最適化 → 可視化 → コストレビュー。全章の道具を一本の基盤に繋ぐ

この章で学ぶこと

前提: ハンズオン 18-1・18-2・18-3 の大半はサンドボックスで完走可・無料枠内。日次自動化(スケジュールクエリ)だけは課金プロジェクト必須(概算 1 円未満)。

この章のゴールと全体アーキテクチャ

ここまでの章で、BigQuery の取り込み・変換・最適化・可視化・運用の技術を個別に学んできました。この章ではそれらを 1 つのプロジェクトとして繋ぎ直します。題材は「疑似的なイベントログを溜め、州別・プラットフォーム別に集計してダッシュボードにする」という小さな分析基盤です。

設計の軸は raw / staging / mart という 3 層のデータセット分割です。

層役割主な操作対応する章
capstone_raw取り込んだそのままの形を保持し、再現性を確保するGCS バッチロード・生成データの CTAS第 2 章・第 3 章
capstone_staging型・表記を整え、ネストされた構造を扱いやすくするJOIN・STRUCT フィールドアクセス・型キャスト第 4 章・第 5 章
capstone_mart分析・BI 向けに集計し、スキャン量とレイテンシを削るPARTITION BY・CLUSTER BY・マテリアライズドビュー第 6 章・第 8 章
取り込み GCS/生成 ロード capstone_raw 整備 capstone_staging 集計/分割 capstone_mart 接続 Data Studio 運用レビュー コスト・品質 権限(承認済みビュー)
図 18-1 raw から staging・mart へ一直線に流れたデータは BI に接続される一方、mart は常にコスト監視と権限設計というもう 1 つの矢印の対象にもなる。

データは 2 つの経路で raw 層に入ります。1 つは Cloud Storage 上の公開 CSV を bq load でそのまま取り込む経路(第 3 章のバッチロード)、もう 1 つは GENERATE_TIMESTAMP_ARRAY で疑似的なイベントログをその場で生成する経路です。後者は本番のストリーミング取り込み(第 12 章)を模した、サンドボックスだけで完結する代替手段だと考えてください。

GCS: us-states.csv bq load(バッチ) capstone_raw.dim_states GENERATE_TIMESTAMP_ARRAY CTAS(生成) capstone_raw.events_raw
図 18-2 capstone_raw は、検証済みの公開 GCS ファイルをそのままロードする経路と、GENERATE_TIMESTAMP_ARRAY で疑似イベントを都度生成する経路の 2 本で供給される。
実務の視点: 3 層に分けたからといって層を増やしすぎると、その分メンテナンスコストが上がります。staging を挟むかどうかの判断基準は「型やネストの整形ロジックを複数の mart テーブルで再利用するか」です。今回のように mart が 1 つしかないなら raw から直接 mart 用の CTAS を書く選択も現実的ですが、この章では第 4 章・第 5 章のネスト展開手法を実際に使う練習として staging を独立させています。データセットの命名規則(capstone_ という接頭辞)はプロジェクトの最初に固定し、途中で変えないようにしてください。

変換の自動化と可視化の位置づけ

staging → mart の再集計を毎日走らせる手段は 2 つあります。テーブル数が少なく依存関係も単純ならスケジュールクエリで十分ですが、テーブルが増えて依存グラフが複雑になると Dataform の ref() による依存解決が効いてきます。どちらの作り方もすでに第 11 章で確認済みなので、この章では設計判断だけを扱います。

変換の依存は単純か? はい(単純) スケジュールクエリ(第 11 章) いいえ(複雑) Dataform(第 11 章)
図 18-3 テーブル数が少なく依存もシンプルならスケジュールクエリ、テーブルが増え依存関係が複雑になるなら Dataform を選ぶ。作り方はどちらも第 11 章で確認済み。

mart 層のさらに先には、頻出の集計をあらかじめ計算しておくマテリアライズドビューがあります。インクリメンタル更新が効いている間、BigQuery はベーステーブル(staging)宛のクエリを自動的にマテリアライズドビューへ書き換えます(スマートチューニング)。ベーステーブルの変更がビューを無効化しうる場合は、ビューではなくベーステーブルを直接読みに行きます。マテリアライズドビューは他のマテリアライズドビューの上にネストできず、外部テーブル・ワイルドカードテーブル・論理ビュー・テーブルスナップショットの上にも作れません。

分析クエリ(集計 SELECT) 変更なし 変更あり mv_platform_daily を参照 events_flat を直接スキャン
図 18-4 events_flat への集計クエリは、ベーステーブルが未変更なら BigQuery が自動的に mv_platform_daily へ書き換え、変更があればベーステーブルを直接読みに行く。

可視化は BigQuery のクエリ結果から直接 BI ツールへ渡します。本書執筆時点(2026-08)の BigQuery 公式ドキュメントでは、以前 Looker Studio と呼ばれていたこの製品は Data Studio という表記に戻っています。ダッシュボードはクエリ結果に基づく一時テーブルを介して作られ、この一時テーブルは最大 24 時間保持されます。Data Studio のチャートは最大 5,000 行までしか可視化できず、BigQuery クエリ自体も 1 回の結果として返せるのは最大 20 MB という制限があるため、mart 層のような集計済みテーブルを接続先に選ぶことが重要です。

コストと権限をどう組み込むか

集計基盤は作って終わりではありません。INFORMATION_SCHEMA.JOBS_BY_PROJECT でどのクエリがどれだけのバイト数・スロットを消費したかを確認できる状態にしておくと、想定外の高コストクエリにすぐ気づけます。権限面では、分析者に raw / staging 層への直接アクセスを与えず、mart 層の承認済みビューだけを見せる設計にします。

分析者 dataViewer capstone_mart 承認済みビュー 委任 capstone_staging 分析者には raw / staging への直接アクセス権を付与しない
図 18-5 分析者には capstone_mart の承認済みビューだけを見せ、ビューが Authorize views で capstone_staging への読み取りを委任する。分析者自身は raw / staging に直接アクセスできない。
実務の視点: 権限設計は「承認済みビュー」「行・列レベルセキュリティ」「別テーブルへの分離」の 3 択です。公式ドキュメントは、別テーブルへの分離が最も安全で柔軟性が低く、行レベルセキュリティは柔軟かつ安全、承認済みビューは柔軟かつ最もパフォーマンスに優れるという住み分けを示しています。分析者ごとに見せる行・列が大きく異なるなら行レベルセキュリティ(第 16 章)、今回のように「raw と staging は見せず mart だけ見せる」という層単位の切り分けなら承認済みビューで十分です。
ハンズオン 18-1|raw 層を組み立てる — GCS バッチロードと疑似イベントログ生成

3 つのデータセットを作り、raw 層に 2 経路でデータを供給します。サンドボックスで完走可・無料枠内(スキャン量は数百 KB 程度)。

  1. raw / staging / mart の 3 データセットを作成します(ロケーションは US)。
bq mk --location=US --dataset PROJECT_ID:capstone_raw
bq mk --location=US --dataset PROJECT_ID:capstone_staging
bq mk --location=US --dataset PROJECT_ID:capstone_mart
  1. 公開 Cloud Storage バケットの CSV をそのままロードします(第 3 章のバッチロード。第 10 章 でも使った検証済みの公開ファイルです)。
bq load --location=US --autodetect --source_format=CSV \
  capstone_raw.dim_states \
  gs://cloud-samples-data/bigquery/us-states/us-states.csv

-- 処理: 数十 KB 程度
SELECT * FROM `PROJECT_ID.capstone_raw.dim_states` LIMIT 5;

期待される出力: name / post_abbr の 2 列で州名の行が返ります。

  1. GENERATE_TIMESTAMP_ARRAY で 1 週間・1 時間刻みの疑似イベントログを生成します。実行前に dry run で処理バイト量を確認する習慣を忘れずに(このクエリは定数からの生成なのでスキャン量はごくわずかです)。
-- 処理: 数百 KB 未満(168 行の生成データ、無料枠内)
CREATE OR REPLACE TABLE `PROJECT_ID.capstone_raw.events_raw` AS
WITH seed AS (
  SELECT ts, seq
  FROM UNNEST(
    GENERATE_TIMESTAMP_ARRAY('2026-07-01 00:00:00', '2026-07-07 23:00:00', INTERVAL 1 HOUR)
  ) AS ts WITH OFFSET seq
)
SELECT
  GENERATE_UUID() AS event_id,
  ts AS event_ts,
  ['CA', 'TX', 'NY', 'WA', 'FL'][OFFSET(MOD(seq, 5))] AS state_abbr,
  ['signup', 'view_item', 'add_to_cart', 'purchase'][OFFSET(MOD(seq, 4))] AS event_name,
  STRUCT(
    ['web', 'ios', 'android'][OFFSET(MOD(seq, 3))] AS platform,
    ARRAY(
      SELECT tag FROM UNNEST(['sale', 'new_user', 'repeat']) AS tag WITH OFFSET o
      WHERE MOD(seq + o, 2) = 0
    ) AS tags
  ) AS client,
  ROUND(RAND() * 200, 2) AS amount_usd
FROM seed;

期待される出力: 168 行(7 日 × 24 時間)。client 列が platform(STRING)と tags(ARRAY<STRING>)を持つ STRUCT になっていることを、スキーマタブで確認してください。この入れ子構造が次のハンズオンで展開する対象です。

後片付け: 3 つのデータセットは次のハンズオンでも使うため、ここでは削除しません。

ハンズオン 18-2|staging → mart — 型整備・フラット化とパーティション+クラスタ化

ネストを展開して staging 層を作り、パーティション・クラスタ化した mart テーブルへ集計します。テーブル作成自体はサンドボックスで完走可・無料枠内。自動化(手順 3)だけは課金プロジェクト必須(概算 1 円未満)。

  1. staging 層でネストを展開し、州名を結合します(第 4 章・第 5 章)。client.platform は STRUCT フィールドへのドット表記で取り出し、tags 配列はそのまま列として残します(後で IN UNNEST で判定するため、ここで展開して行を増やしません)。
-- 処理: 数百 KB 未満
CREATE OR REPLACE TABLE `PROJECT_ID.capstone_staging.events_flat` AS
SELECT
  e.event_id,
  DATE(e.event_ts) AS event_date,
  e.event_ts,
  e.state_abbr,
  s.name AS state_name,
  e.event_name,
  e.client.platform AS platform,
  e.client.tags AS tags,
  e.amount_usd
FROM `PROJECT_ID.capstone_raw.events_raw` AS e
LEFT JOIN `PROJECT_ID.capstone_raw.dim_states` AS s
  ON e.state_abbr = s.post_abbr;

期待される出力: 168 行(raw と 1 対 1)。platform がフラットな STRING 列になり、state_name が結合できていることを確認してください。

  1. mart 層をパーティション + クラスタ化して作ります(第 6 章)。配列の中身は IN UNNEST で判定し、行を増やさずに集計します。
-- 処理: 数百 KB 未満
CREATE OR REPLACE TABLE `PROJECT_ID.capstone_mart.daily_state_events`
PARTITION BY event_date
CLUSTER BY state_abbr, event_name
AS
SELECT
  event_date,
  state_abbr,
  state_name,
  event_name,
  COUNT(*) AS event_count,
  COUNTIF('repeat' IN UNNEST(tags)) AS repeat_tagged_count,
  ROUND(SUM(amount_usd), 2) AS total_amount_usd
FROM `PROJECT_ID.capstone_staging.events_flat`
GROUP BY event_date, state_abbr, state_name, event_name;
  1. この再集計を自動化します。テーブル数は 3 つ、依存も raw → staging → mart の一直線なので、図 18-3 の判断基準に従いスケジュールクエリで十分です(第 11 章)。
bq query \
  --use_legacy_sql=false \
  --display_name="capstone_daily_mart_refresh" \
  --schedule="every 24 hours" \
  --destination_table=PROJECT_ID:capstone_mart.daily_state_events \
  --replace=true \
'SELECT
   event_date, state_abbr, state_name, event_name,
   COUNT(*) AS event_count,
   COUNTIF("repeat" IN UNNEST(tags)) AS repeat_tagged_count,
   ROUND(SUM(amount_usd), 2) AS total_amount_usd
 FROM `PROJECT_ID.capstone_staging.events_flat`
 GROUP BY event_date, state_abbr, state_name, event_name'

サンドボックスで試す場合は、手順 3 のスケジュール登録の代わりに、手順 2 の CREATE OR REPLACE TABLE をそのままクエリエディタで手動実行する運用に置き換えてください(スケジュールクエリは BigQuery Data Transfer Service の機能であり、サンドボックスでは利用できません)。

後片付け: 3 つのデータセットは次のハンズオンでも使うため削除しません。スケジュールクエリを登録して確認が済んだら、BigQuery Studio の Scheduled queries 画面から削除してください。

ハンズオン 18-3|マテリアライズドビューと Data Studio でダッシュボード化する

mart をさらに高速化するマテリアライズドビューを作り、クエリ結果を Data Studio に接続します。いずれもサンドボックスで完走可・無料枠内。

  1. プラットフォーム別・日別の集計をマテリアライズドビューにします。
-- 処理: 初回のみ数百 KB 未満。以降はベーステーブルの差分のみ
CREATE MATERIALIZED VIEW `PROJECT_ID.capstone_mart.mv_platform_daily`
AS
SELECT
  event_date,
  platform,
  COUNT(*) AS event_count,
  ROUND(SUM(amount_usd), 2) AS total_amount_usd
FROM `PROJECT_ID.capstone_staging.events_flat`
GROUP BY event_date, platform;
  • ベーステーブルに同じ集計を投げ、自動書き換え(スマートチューニング)を確認します。ベーステーブルが未変更なら、実行タブの Execution details でジョブが mv_platform_daily を参照していることが分かります。
  • SELECT platform, COUNT(*) AS event_count
    FROM `PROJECT_ID.capstone_staging.events_flat`
    GROUP BY platform;
  • mart テーブルを Data Studio に接続します。クエリエディタで抽出用の SQL を実行します。
  • SELECT event_date, state_abbr, state_name, event_count, total_amount_usd
    FROM `PROJECT_ID.capstone_mart.daily_state_events`
    ORDER BY event_date;
  • クエリ結果セクションの「Open in」から「Data Studio」を選び、Welcome 画面で「Get Started」、「Authorize Data Studio access」ページで「Authorize」をクリックします。レポートエディタが開いたら、棒グラフや表チャートを追加して結果を確認してください。
  • 後片付け: 3 つのデータセットは次のハンズオンでも使うため削除しません。作成した Data Studio のレポートは、他者と共有していなければ残しておいても課金は発生しません(BigQuery 側のクエリ実行分のみが課金対象です)。

    ハンズオン 18-4|コスト・品質レビューと権限設計、そして後片付け

    INFORMATION_SCHEMA でコストを確認し、承認済みビューで権限を設計してから、作った 3 データセットをすべて削除します。サンドボックスで完走可・無料枠内(メタデータ照会とビュー作成のみ)。

    1. 直近 1 日のジョブをコスト順に確認します。
    -- 処理: メタデータのみ(課金対象バイト数はごくわずか)
    SELECT
      user_email,
      job_type,
      statement_type,
      ROUND(total_bytes_processed / POW(1024, 3), 4) AS gb_processed,
      ROUND(total_slot_ms / 1000, 1) AS slot_seconds,
      query
    FROM `PROJECT_ID`.`region-us`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
    WHERE
      creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)
      AND state = 'DONE'
      AND REGEXP_CONTAINS(query, r'capstone_')
    ORDER BY total_bytes_processed DESC
    LIMIT 20;

    期待される出力: このハンズオンで実行してきたクエリが一覧され、どのクエリが最もバイト数・スロットを消費したかがひと目で分かります。Data Studio 経由のジョブには looker_studio_report_id / looker_studio_datasource_id という labels が付くため、BI 由来のコストだけを切り分けたいときはこの labels で絞り込めます。

  • mart 層の一部だけを見せる承認済みビューを作ります(ビューの保存先はソースデータセットと必ず別にします)。
  • CREATE VIEW `PROJECT_ID.capstone_mart.events_flat_view` AS
    SELECT event_date, state_abbr, state_name, event_name, platform, amount_usd
    FROM `PROJECT_ID.capstone_staging.events_flat`;
  • コンソールで capstone_staging データセットを開き、Details ペインの「Sharing」→「Authorize views」から、上記ビューの完全修飾名(PROJECT_ID.capstone_mart.events_flat_view)を入力して「Add authorization」をクリックします。
  • 分析者グループには capstone_mart データセットへの BigQuery Data Viewer とプロジェクトの Job User 権限だけを付与し、capstone_raw / capstone_staging への権限は一切付与しません。
  • 後片付け(この章の最終後片付け):

    bq rm -r -f -d PROJECT_ID:capstone_mart
    bq rm -r -f -d PROJECT_ID:capstone_staging
    bq rm -r -f -d PROJECT_ID:capstone_raw

    スケジュールクエリを登録したままの場合は、BigQuery Studio の Scheduled queries 画面からも削除してください。マテリアライズドビューと承認済みビューは、データセットの削除と同時に削除されます。

    つまずきポイント

    スケジュールクエリがサンドボックスで動かない: スケジュールクエリは BigQuery Data Transfer Service の機能であり、サンドボックスが明示的にサポートしない 3 機能(ストリーミングデータ・DML・BigQuery Data Transfer Service)の 1 つに含まれます。サンドボックスで試す場合は、ハンズオン 18-2 の CREATE OR REPLACE TABLE を手動で再実行する運用に置き換えてください。
    マテリアライズドビューを別のマテリアライズドビューの上に作ろうとしてエラーになる: マテリアライズドビューはマテリアライズドビューの上にネストできません。集計をさらに集計したい場合は、通常のテーブルかビューを間に挟んでください。外部テーブル・ワイルドカードテーブル・論理ビュー・テーブルスナップショットの上にも作れません。
    承認済みビューが保存できない・認可が効かない: ビューを保存するデータセットは、ソースクエリが読みに行くデータセット(今回は capstone_staging)と必ず別でなければなりません。また、ビュー側の承認だけでは不十分で、分析者自身にもビューを含むデータセットへの閲覧権限とジョブ実行権限(roles/bigquery.jobUser)を別途付与する必要があります。
    後片付け忘れで capstone_raw / staging / mart が残り続ける: サンドボックスのテーブルは 60 日で自動削除されますが、課金プロジェクトでは残り続け、スケジュールクエリも動き続けて課金が発生します。ハンズオン 18-4 の最後で 3 つのデータセットとスケジュールクエリを必ず削除してください。

    理解度チェック

    Q1. raw / staging / mart の 3 層にデータセットを分ける狙いは何ですか?

    raw は取り込んだそのままの形を保持して再現性を確保し、staging で型・表記・ネスト構造を整え、mart で分析・BI 向けに集計してクエリを高速化するためです。層ごとに役割を分けることで、どこかの層で問題が起きても手前の層から作り直せます。

    Q2. マテリアライズドビューがクエリを自動的に書き換える(スマートチューニング)ための前提条件は何ですか?

    インクリメンタル更新が有効なマテリアライズドビューであり、かつベーステーブルの変更がまだビューを無効化していない状態であることです。ベーステーブルの変更がビューを無効化しうる場合、BigQuery はビューではなくベーステーブルを直接読みに行きます。

    Q3. 承認済みビューを作るとき、ビューを保存するデータセットをソースデータのデータセットと分ける理由は何ですか?

    承認済みビューは「ビューには見せるがソースデータセットには直接アクセスさせない」ことが目的だからです。同じデータセットに保存すると、ビューへのアクセス権がそのままソースデータへの直接アクセス権にもなってしまい、権限を分離できません。

    Q4.(シナリオ)サンドボックスのまま staging → mart の再集計を毎日自動化したい。最も適切な対処は?
    A. DML で自動更新するトリガーを書く B. 再集計クエリを手動で定期的に再実行する運用に切り替える C. Dataform のワークフロースケジュールを使う D. Storage Write API でストリーミング挿入する

    B。 サンドボックスはストリーミングデータ・DML・BigQuery Data Transfer Service を明示的にサポートしておらず、スケジュールクエリも Dataform のスケジュール実行もこの Data Transfer Service の機能に依存するため使えません。A はサンドボックスで禁止されている DML そのものです。C はスケジュールクエリと同様に課金プロジェクトが必要です。D はストリーミングデータ自体がサンドボックス非対応です。

    Q5.(シナリオ)分析チームが増え、raw と staging には一切アクセスさせず、mart 層の日次集計だけを見せたい。最も適切な権限設計は?
    A. 分析者全員にプロジェクトの BigQuery Data Viewer ロールを付与する B. capstone_mart に閲覧用ビューを作り、Authorize views で capstone_staging への読み取りをビューに委任した上で、分析者には capstone_mart のデータセットアクセス権とジョブ実行権限だけを付与する C. 分析者ごとにサービスアカウントの鍵ファイルを配布する D. raw テーブルの内容をアクセス制御なしの別プロジェクトにコピーする

    B。 プロジェクト全体への Data Viewer を配る A は raw / staging も見えてしまい層の切り分けが機能しません。C は鍵管理の負担とセキュリティリスクが増すだけで権限設計の解決になりません。D はガバナンスを放棄しています。B が承認済みビューの標準的な使い方で、raw と staging への直接アクセス権を与えずに mart の集計だけを見せられます。

    まとめと次章へ

    発展課題として、raw 層を Storage Write API によるストリーミング取り込みに置き換える(第 12 章)、mart 層に AI 関数を組み合わせてイベントの要約や異常検知を加える(第 14 章)といった拡張が考えられます。本書はここで一区切りですが、この章で組んだ 3 層構造はどの章の技術を差し替えても壊れない骨格として、そのまま実務に持ち出せます。