Google Cloud BigQueryサンドボックスでできること、できないこと

# はじめに

本記事では、これからBigQueryの学習を始める初心者向けに、BigQueryサンドボックスでできること、できないことをお伝えします。

## この記事では触れないこと

* Googleアカウントの作り方
* BigQueryの説明
* BigQueryのプロジェクト作成手順（[こちら](https://cloud.google.com/bigquery/docs/sandbox?hl=ja)を参考に作成しましょう）

## BigQueryサンドボックスとは

クレジットカードなどの登録なしで、すぐに**無償で**利用できるBigQueryです。
無償であるが故に使えない機能も多くあります。Geminiにサンドボックスと有償版の違いを聞いてみたところ、ざっと以下の点が挙げられました。

<details><summary>Geminiによるサンドボックスと有償版の相違点説明</summary>

* 費用:
    * サンドボックス: 無料で利用できます。ただし、無料枠には制限があります。
    * 有償版: ストレージ容量とクエリ処理量に応じて課金されます。
* 無料枠:
    * サンドボックス: 毎月 10 GB のアクティブ ストレージと 1 TB のクエリ処理が無料です。
    * 有償版: 毎月 10 GB のストレージと 1 TB のクエリ処理が無料枠として提供されますが、超過分は課金されます。
* 機能制限:
    * サンドボックス: 一部の機能（データのストリーミング、DMLステートメント、BigQuery Data Transfer Serviceなど）が利用できません。
    * 有償版: BigQueryのすべての機能を利用できます。
* データ保持期間:
    * サンドボックス: すべてのテーブルとパーティションは60日後に自動的に削除されます。
    * 有償版: データ保持期間に制限はありません。
* サポート:
    * サンドボックス: テクニカルサポートは提供されません。
    * 有償版: Google Cloudのテクニカルサポートを利用できます。
* 利用開始:
    * サンドボックス: クレジットカード情報の登録は不要で、すぐに利用を開始できます。
    * 有償版: 課金アカウントの設定が必要です。

</details>

# 事前準備

テーブルを作成する前に、データセット（DBオブジェクトなどを格納するコンテナ）を作成します。

## データセット作成

1. BigQuery Studioでプロジェクト名の右にあるハンバーガーメニューをクリックし、「データセットを作成」をクリック
![スクリーンショット 2025-04-28 13.34.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/510afbfa-7adb-42a8-b6a7-79f1743a75f0.png)
1. データセットIDを入力する
※ロケーションタイプとリージョンはお好みで。2025/4/28時点では、マルチリージョンにするとロケーションはヨーロッパか南北アメリカしか選べない模様
![スクリーンショット 2025-04-28 13.39.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/15d37ebc-8e03-4fb6-8035-807d3e5d7d18.png)
1. 「データセットを作成」ボタンをクリックしたら、事前準備完了

# やったこと1：DDLでテーブル作成

DDLを使用して、テーブルを作成、データを投入してみます。

## テーブル作成

ブラウザのGUIで、以下のSQLを実行しました。
```sql
CREATE TABLE league_one.teams (
  id    INT64  NOT NULL
 ,name  STRING NOT NULL
 ,PRIMARY KEY(id) NOT ENFORCED
)
```
実行すると、以下の画面になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/5b1515c3-2700-4143-8b23-cc989f4a6e4e.png)
DDLは正常に実行完了しました。

:::note warn
DDLでは主キーに対し`NOT ENFORCED`オプションを指定していますが、2025/4/28時点では一意制を強制することができません。よって、DDL内で主キーを設定する場合は`NOT ENFORCED`オプションが必須になります。
公式によると、一意性の担保はユーザー側でやってね、とのこと。詳細について知りたい方は[こちら](https://cloud.google.com/blog/ja/products/data-analytics/join-optimizations-with-bigquery-primary-and-foreign-keys/)を読んでみましょう。
:::

## データ投入

以下のSQLを実行します。
```sql
INSERT INTO league_one.teams (
  id
 ,name
) VALUES (
  1
 ,'浦安D-Rocks'
)
```
実行すると、以下の画面になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/9b3f681a-bad2-43be-9085-370ac9fce7f4.png)
「Billing has not been enabled for this project. （中略）DML queries are not allowed in the free tier. Set up a billing account to remove this restriction.」と怒られました。
要約すると、「サンドボックスではDMLの実行は許さない」みたいです。

## 検索はできるのか

「DMLは許さん」と言われましたが、SELECT文もDMLです。
それならSELECT文でデータ検索もできないのか、試してみます。
```sql
SELECT
  *
FROM
  league_one.teams
```
実行結果はこちら。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/ebcc9d76-6d8b-4bb1-9757-279f763f5d1d.png)
DMLの中でも、SELECT文だけなら許される様子。

# やったこと2：空のテーブル作成

今度はGUIベースでテーブルを作成します。

## テーブル作成

データセット右にあるハンバーガーメニューをクリックして、「テーブル作成」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/a61f5ed2-b77e-431d-834c-85695aa3979d.png)
まずは「ソース」に「空のテーブル」を設定（デフォルト値）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/8794776b-6622-4582-9c01-e91d2fdc3d4c.png)
次に「テーブル名」と「スキーマ」を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/80fa67d3-0154-4d73-9104-477679c127dd.png)
入力が完了したら、「テーブルを作成」ボタンをクリックして、作成完了です。

:::note
Oracle Databaseを使用している人にとって「スキーマ」でテーブルのフィールドを設定することに対し、違和感があると思います。
Gemini曰く、RDBのカラムとBigQueryのスキーマでは以下の違いがあるとのこと。
* RDBのカラム: テーブル内の個々の属性（名前とデータ型）。
* BigQueryのスキーマ: テーブルまたはビューの構造全体を定義し、カラムの名前、データ型、モード、ネストされた構造、説明などを含みます。
:::

## データ投入

「やったこと1」と同様、できません。

# やったこと3：CSVアップロードでテーブル作成

GUI上でCSVファイルをアップロードすることで、テーブルを作成します。
データセット右にあるハンバーガーメニューをクリックして、「テーブル作成」をクリックし、「ソース」に「アップロード」を設定。
下記内容のCSVファイルを設定します。
```plaintext:league_one_teams
1,浦安D-Rocks
2,コベルコ神戸スティーラーズ
3,静岡ブルーレヴズ
4,東芝ブレイブルーパス東京
5,三菱重工相模原ダイナボアーズ
6,横浜キヤノンイーグルス
7,クボタスピアーズ船橋・東京ベイ
8,埼玉パナソニックワイルドナイツ
9,東京サントリーサンゴリアス
10,トヨタヴェルブリッツ
11,三重ホンダヒート
12,リコーブラックラムズ東京
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/9252edca-ed9d-4672-9182-a7670cc65c91.png)
テーブル名とスキーマの設定は「やったこと2」と同じ内容で、「テーブルを作成」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/1ca901b7-5377-47cf-aa9c-03509c5be0d2.png)
「teams」テーブルが作成されました。

## データ検索

以下のクエリでデータを検索してみます。
```sql
SELECT
  *
FROM
  league_one.teams
```
実行結果は以下のとおり。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/df90e39d-858c-4fb7-af7b-267e1a6a5c1f.png)
データがきちんと取れました。

## データ更新・削除

INSERT文はサンドボックスで実行できませんでした。恐らく、UPDATE文、DELETE文も実行できないと考えられますが、試してみました。

### UPDATE

SQLは以下の通り

```sql:UPDATE文
UPDATE league_one.teams
SET name = 'D-Rocks'
WHERE id = 1
```
実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/2b39d6b0-2d13-4d37-a62b-9f4d9e208caf.png)
案の定、怒られました。

### DELETE

SQLは以下の通り

```sql:DELETE文
DELETE FROM league_one.teams
WHERE id = 1
```
実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/49a8cb35-3d3f-4830-92ee-bc331eac4a9d.png)
こちらも案の定、怒られました。

## 関連するテーブルも追加してみる

今度は以下CSVを作成し、「members」テーブルをCSVアップロードで作成します。
```plaintext:canon_eagles_members.csv
南 友紀,1993-10-23,A,6
ゴールディング 光士,2003-11-16,A,6
杉本 達郎,1996-09-30,A,6
三好 優作,1998-05-14,A,6
松岡 将大,1997-07-31,A,6
岡部 崇人,1995-02-19,A,6
祝原 涼介,1996-10-06,A,6
小川 寛大,1998-11-29,A,6
川村 慎,1987-08-06,A,6
庭井 祐輔,1991-10-22,A,6
中村 駿太,1994-02-28,A,6
平石 颯,2002-02-09,A,6
トム・ジェフリーズ,1999-03-15,B,6
コルマック・ダリー,1998-05-01,B,6
リアキマタギ・モリ,1990-01-04,A,6
久保 克斗,1998-11-07,A,6
安井 龍太,1989-12-06,A,6
マシュー・フィリップ,1994-03-07,C,6
ジャンドレ・ラブスカフニ,1999-07-17,B,6
ビリー・ハーモン,1994-12-23,B,6
古川 聖人,1996-12-06,A,6
シオエリ・ヴァカラヒ,1998-05-15,A,6
嶋田 直人,1991-05-21,A,6
レキマ・ナサミラ,1998-12-27,A,6
アミニアシ・ショー,2000-03-13,A,6
シオネ・ハラシリ,1999-10-15,A,6
アマナキ・レレイ・マフィ,1990-01-11,A,6
吉田 大亮,1998-07-20,A,6
ファフ・デクラーク,1991-10-19,C,6
松木 勇斗,1999-11-26,A,6
山菅 一史,1997-12-03,A,6
天野 寿紀,1990-10-27,A,6
荒井 康植,1993-05-14,A,6
土永 旭,2003-01-09,A,6
小倉 順平,1992-07-11,A,6
田村 優,1989-01-09,A,6
武藤 ゆらぎ,2001-07-30,A,6
竹澤 正祥,1995-05-05,A,6
松井 千士,1994-11-11,A,6
高木 一成,1997-06-23,A,6
石田 吉平,2000-04-28,A,6
江藤 良,2000-01-09,A,6
ヴィリアメ ・タカヤワ,1997-02-21,A,6
梶村 祐介,1995-09-13,A,6
ジェシー・クリエル,1994-02-15,C,6
南橋 直哉,1989-08-10,A,6
田畑 凌,1996-07-05,A,6
猿田 湧,1998-06-04,A,6
普久原 琉,2000-11-03,A,6
ブレンダン・オーウェン,1996-09-23,B,6
```
スキーマ設定は以下の通り。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/902eb761-21f5-477a-9b24-3766a59d65c8.png)

テーブルの作成が完了したら、以下のSQLを実行します。
```sql
SELECT
    tm.name  AS team_name
   ,mem.name AS member_name
FROM
    league_one.teams tm
INNER JOIN
    league_one.members mem
        ON  mem.team_id = tm.id
        AND mem.category = 'C'
WHERE
    tm.id = 6
```
実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551450/7300352b-ff8d-4e72-970f-03caf0d3e9b3.png)
JOINを含むクエリも実行できました。

# まとめ

以下、本記事主題のできること、できないことのまとめです。

## できること
* DDLによるテーブル作成
* GUIからのテーブル作成
* SELECT文実行

## できないこと
* INSERT文実行
* UPDATE文実行
* DELETE文実行