BigQuery SQLコマンド集-2023年度版
- データセットの管理
- テーブルの管理
- パーティーション分割
- クラスタリング
- テーブルスナップショット
- ビュー, マテリアライズド ビュー
- ルーティン
- ユーザー定義関数(UDF)
- テーブル関数
- リモート関数
- 検索インデックスの管理
- Cloud Storageにクエリを行う
データセットの管理
データセットを作成する
CREATE SCHEMA `PROJECT_ID.DATASET_ID` OPTIONS ( description = 'データセット説明', location = 'US' )
location例:
US: マルチリージョン
asia-northeast1: 東京
BigQuery のロケーション | Google Cloud
プロジェクトの全データセット名をリスト表示
SELECT schema_name FROM `PROJECT_ID`.INFORMATION_SCHEMA.SCHEMATA
INFORMATION_SCHEMA.SCHEMATA ビューから全データセット名、作成日時、更新日時等が取得できる
データセットの削除
DROP SCHEMA IF EXISTS `PROJECT_ID.DATASET_ID`
削除前にデータセット内のテーブルとビューを削除すること
テーブルの管理
テーブルの作成
CREATE TABLE `PROJECT_ID.DATASET_ID.TABLE_ID` ( x INT64 OPTIONS (description = '列説明'), y STRING ) OPTIONS ( description = 'テーブル説明' )
テーブルのメタデータの表示
SELECT * FROM `PROJECT_ID.DATASET_ID.INFORMATION_SCHEMA.TABLES`;
テーブル名、テーブルタイプ、作成日時、作成したコマンド(DDL)などが表示される
テーブルの削除
DROP TABLE `PROJECT_ID.DATASET_ID.TABLE_ID`
テーブルの外部エクスポート
EXPORT DATA OPTIONS ( # 出力先URI uri = 'gs://bucket/folder/*.csv', format = 'CSV', overwrite = true, header = true, field_delimiter = ';') AS ( SELECT * FROM `PROJECT_ID.DATASET_ID.TABLE_ID` );
- エクスポート先はGoogle Cloud Storageのみ
- CSV、JSON、Avro、Parquet(プレビュー)形式対応
- 1ファイル最大 1 GB 。1 GB を超えるデータをエクスポートする場合は複数ファイルに分割される
- Cloud Storageに書き込む権限必要 参考:https://cloud.google.com/bigquery/docs/exporting-data?hl=ja
ネストされた列と繰り返し列を含むテーブルを作成する
CREATE TABLE `PROJECT_ID.DATASET_ID.TABLE_ID` ( id INT64, addresses ARRAY < STRUCT < address STRING, status STRING > > )
- 補足: 各IDごとに複数住所(address)とステータス(status)を格納できるテーブルを定義。
ネストデータ:STRUCT
繰り返しデータ:ARRAY - BigQueryでは多くの場合、ネストされた繰り返しフィールドを使用して単一のテーブルにクエリを実行すると、複数のテーブルを結合する場合よりも効率的に処理されます。
ネストされたレコードの挿入
INSERT INTO `PROJECT_ID.DATASET_ID.TABLE_ID` (id, addresses) VALUES ("1",[("past","Tokyo"),("now","Kanagawa")]), ("2",[("past","Chiba"),("now","Saitama")])
ネストされた繰り返し列(addresses)に対して、特定の位置にある ARRAYの値を取得する
SELECT # 1番目の値 addresses[offset(0)].address FROM `PROJECT_ID.DATASET_ID.TABLE_ID` ORDER BY id
- offset(index): インデックスは 0 から始まります。範囲外の場合はエラーが発生します。
- safe_offset(index): インデックスは 0 から始まります。範囲外の場合にNULLを返します。
- ordinal(index): インデックスは 1 から始まります。範囲外の場合はエラーが発生します。
- safe_ordinal(index): インデックスは 1 から始まります。範囲外の場合にNULLを返します。
テーブルに空の列を追加する
ALTER TABLE `PROJECT_ID.DATASET_ID.TABLE_ID` ADD COLUMN new_column STRING;
テーブルの列の名前を変更する
ALTER TABLE `PROJECT_ID.DATASET_ID.TABLE_ID` RENAME COLUMN old_column TO renamed_column;
テーブルの列のデフォルト値を変更する
ALTER TABLE `PROJECT_ID.DATASET_ID.TABLE_ID` ALTER COLUMN x SET DEFAULT 0;
列のデフォルト値を削除する
ALTER TABLE `PROJECT_ID.DATASET_ID.TABLE_ID` ALTER COLUMN x DROP DEFAULT;
パーティーション分割
(時間単位列)パーティション分割テーブルを作成する
CREATE TABLE `PROJECT_ID.DATASET_ID.TABLE_ID` (transaction_id INT64, transaction_date DATE) PARTITION BY # transaction_date列で分割 transaction_date OPTIONS ( require_partition_filter = TRUE )
- パーティション:特定の範囲でテーブルを分割する。パーティション作成するとテーブルの一部のみスキャン→コスト削減、パフォーマンス向上
参考:パーティション列からレコードを絞り込むことをプルーニングと呼ぶ。条件式が定数式であること等、プルーニングが動作するためにはいくつか条件あり。詳細 - 整数範囲、時間単位列、取り込み時間パーティショニングの3種類
- オプションでrequire_partition_filter = TRUE にすると、SELECT時のフィルター指定(where句)を強制できる→where句を指定しないとエラー
(取り込み時間)パーティション分割テーブルを作成する
CREATE TABLE `PROJECT_ID.DATASET_ID.TABLE_ID` (transaction_id INT64) PARTITION BY _PARTITIONDATE
PARTITIONTIME (あるいはPARTITIONDATE) 列は、レコードの取り込み時間/日付がレコード挿入時に自動的に格納される。
パーティション フィルタ要件を更新する
ALTER TABLE `PROJECT_ID.DATASET_ID.TABLE_ID` SET OPTIONS ( require_partition_filter = FALSE);
クラスタリング
クラスタ化テーブルを作成する
CREATE TABLE `PROJECT_ID.DATASET_ID.TABLE_ID` ( customer_id STRING, product_id STRING, transaction_amount NUMERIC ) CLUSTER BY # クラスタリング列 customer_id,product_id;
- 最大 4 つのクラスタリング列を指定可能。
- 指定した列の順序によって、データの並べ替え順序が決まる。最も頻繁にフィルタリングまたは集計される列を最初に置く
クラスタリング列をWHERE句でフィルタリングする
SELECT SUM(x) FROM `PROJECT_ID.DATASET_ID.TABLE_ID` WHERE customer_id = "1" AND product_id = "002"
- フィルタリング順序はクラスタリング指定列の順序になっている必要あり(例:上記customer_idとproduct_idの順序を逆にするとクエリが最適化されない)
- パーティション同様、複雑な(動的に値が決まる)フィルタ式ではクラスタ化列を使用しない
テーブルクローンを作成する
CREATE TABLE `PROJECT_ID.DATASET_ID.CRONED_TABLE_ID` CLONE `PROJECT_ID.DATASET_ID.BASE_TABLE_ID`;
- テーブルクローン:ベーステーブルの軽量で書き込み可能なコピー
- 標準テーブル同様にクエリ、パーティション等可能
- ビューや外部テーブルのクローンは不可(コピーと同じ)
- ベーステーブルorクローンのデータを変更しても互いに反映されない(独立したテーブル)
- コピーとの違いはストレージ料金。クローンは新たに変更・追加されたデータ分のみ課金される
テーブルクローン概要
テーブルスナップショット
24 時間後に期限切れになるテーブルのスナップショットを作成
CREATE SNAPSHOT TABLE `PROJECT_ID.DATASET_ID.SNAPSHOT_TABLE_ID` CLONE `PROJECT_ID.DATASET_ID.BASE_TABLE_ID` OPTIONS ( expiration_timestamp = TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 24 HOUR) );
- テーブルスナップショット:別のテーブル(ベーステーブル)の軽量で読み取り専用のコピー
- ベーステーブルと異なるデータセット内に作成することで、ベーステーブルのデータセットが誤って削除された場合でも、テーブル スナップショットからベーステーブルを復元できます
- 過去 7 日間の任意の時点におけるテーブルのスナップショットを作成することが可能
テーブル スナップショットを新しいテーブルに復元
CREATE TABLE `PROJECT_ID.DATASET_ID.TABLE_ID` CLONE `PROJECT_ID.DATASET_ID.SNAPSHOT_TABLE_ID`
テーブル スナップショットのメタデータを取得する
select * from `PROJECT_ID.DATASET_ID.INFORMATION_SCHEMA.TABLE_SNAPSHOTS` where table_name = 'some_table_snapshot';
テーブル スナップショットをコピーする
CREATE SNAPSHOT TABLE `PROJECT_ID.DATASET_ID.COPIED_SNAPSHOT_TABLE_ID` CLONE `PROJECT_ID.DATASET_ID.SNAPSHOT_TABLE_ID`;
テーブル スナップショットを作成するプロセスと似ています。相違点は、ソーステーブルとしてコピー元のテーブル スナップショットを指定することです
テーブル スナップショットを削除する
DROP SNAPSHOT TABLE `PROJECT_ID.DATASET_ID.SNAPSHOT_TABLE_ID`;
ビュー, マテリアライズド ビュー
ビューを作成する
CREATE VIEW `PROJECT_ID.DATASET_ID.TABLE_VIEW_ID` ( customer_id, transaction_amount_sum) AS ( SELECT customer_id, SUM(transaction_amount) FROM `PROJECT_ID.DATASET_ID.TABLE_ID` GROUP BY customer_id )
ビューを照会するたびに、ビューを定義しているクエリが実行されます。大規模なビューや計算負荷の高いビューを頻繁にクエリする場合は、マテリアライズド ビューの作成を検討
ビューを実行する
SELECT * FROM `PROJECT_ID.DATASET_ID.TABLE_VIEW_ID`
テーブルと同じ方法でビューにクエリを実行します。
マテリアライズド ビューを作成する
CREATE MATERIALIZED VIEW `PROJECT_ID.DATASET_ID.TABLE_MV_ID` AS ( SELECT customer_id as id, SUM(transaction_amount) as amount_sum FROM `PROJECT_ID.DATASET_ID.TABLE_ID` GROUP BY customer_id )
・マテリアライズド ビューはクエリの結果を定期的にキャッシュに保存するため、同じデータをベーステーブルから取得するクエリよりも高速で、消費するリソースも少なくて済みます 詳細
マテリアライズド ビューを変更する(例:自動更新を60分ごとにする)
ALTER MATERIALIZED VIEW `PROJECT_ID.DATASET_ID.TABLE_MV_ID` SET OPTIONS (enable_refresh = true, refresh_interval_minutes = 60)
デフォルトではマテリアライズド ビューは以下2つのタイミングで自動更新される
1) 30 分ごとに更新
2) ベーステーブルの変更から 5 分以内に自動的に更新。ただし前の更新から 30 分以内に更新されることはない。
変更の例としては、行の挿入や行の削除があります。自動更新をオフにするには、enable_refresh を false に設定します
マテリアライズド ビューを削除する
DROP MATERIALIZED VIEW `PROJECT_ID.DATASET_ID.TABLE_MV_ID`
ルーティン
ルーティンを作成する (プロシージャ例:変数idを設定し、INSERT を実行して、テキスト文字列として結果を表示する)
CREATE OR REPLACE PROCEDURE `PROJECT_ID.DATASET_ID`.create_customer() BEGIN DECLARE id STRING; # 36文字のランダム英数字を作成 SET id = GENERATE_UUID(); INSERT INTO `PROJECT_ID.DATASET_ID.TABLE_ID` (customer_id) VALUES (id); SELECT FORMAT("Created customer %s", id); END
ルーティン:ストアド プロシージャ、ユーザー定義関数(UDF)、テーブル関数のいずれか
作成したルーティン(プロシージャ)を呼び出す
CALL `PROJECT_ID.DATASET_ID`.create_customer();
データセット内のルーティンを一覧表示する
SELECT routine_name, routine_type FROM `PROJECT_ID.DATASET_ID.INFORMATION_SCHEMA.ROUTINES`
ルーティンの本文を表示する
SELECT routine_definition FROM `PROJECT_ID.DATASET_ID.INFORMATION_SCHEMA.ROUTINES` WHERE routine_name = 'create_customer';
ルーティンを削除する
DROP PROCEDURE IF EXISTS `PROJECT_ID.DATASET_ID`.create_customer
ユーザー定義関数(UDF)
SQLでUDFを作成(例:第1引数に4を足して第2引数で割った値をFLOAT型で返却する)
CREATE TEMP FUNCTION AddFourAndDivide(x INT64, y INT64) RETURNS FLOAT64 AS ( (x + 4) / y ); -- UDF関数の呼び出し SELECT val, AddFourAndDivide(val, 2) FROM UNNEST([2,3,5,8]) AS val;
- SQL式または JavaScriptコードを使用して関数を作成できます。
- 永続的または一時的のいずれかとして定義できます。永続的な関数の場合ROUTINEとして作成可能。
- UNNEST():配列を受け取り各要素を一行ずつ取り出す
JavascriptでUDFを作成する
CREATE TEMP FUNCTION multiplyInputs(x FLOAT64, y FLOAT64) RETURNS FLOAT64 LANGUAGE js AS r""" m = x*y; return m; """; SELECT multiplyInputs(2,3);
raw文字列(r""" ... """)でjavascriptを記載する
テーブル関数
テーブル関数の作成 (例: customer_idが一致するレコードを抽出するテーブル関数 table_function)
CREATE OR REPLACE TABLE FUNCTION `PROJECT_ID.DATASET_ID`.table_function ( id STRING ) AS ( SELECT * FROM `PROJECT_ID.DATASET_ID_TABLE_ID` WHERE customer_id = id )
テーブル関数:パラメータ付きビューのこと
テーブル関数の使用
SELECT * FROM `PROJECT_ID.DATASET_ID`.table_function("1")
リモート関数
リモート関数を作成する
# 作成例:Cloud Functionsにデプロイしたリモート関数の引数の合計を返す e.g. calls = [[1,2,3], [2,3,4]] →[[6], [9]] # Cloud Function(ver2)で以下関数をデプロイ import functions_framework @functions_framework.http def batch_add(request): try: return_value = [] request_json = request.get_json() print('request_json', request_json) calls = request_json['calls'] for call in calls: return_value.append(sum([int(x) for x in call if x is not None])) # callsの要素数と同じreturn_valueリストを返却 response = { "replies": return_value } return response except Exception as e: return { "errorMessage": str(e) } --- リモート関数の作成(事前にCloud Functionsと同じリージョンの接続を作成する必要あり) CREATE FUNCTION `PROJECT_ID.DATASET_ID`.remote_add(x INT64, y INT64) RETURNS INT64 REMOTE WITH CONNECTION `PROJECT_ID.LOCATION.CONNECTION_NAME` OPTIONS ( endpoint = 'ENDPOINT_URL' )
- リモート関数:Cloud Functions, Cloud Runにデプロイされた関数
- Cloud FunctionsはHTTPトリガーで作成
- 接続はcloud Functionsと同じリージョンで作成し、IAMから"Cloud Run Invoker"の権限を与える(ver2の場合)
検索インデックスの管理
検索インデックスを作成 (例:Logsテーブルを作成し、全列にLOG_ANALYZERの検索インデックスを作成する)
-- テストテーブルの作成 CREATE TABLE `PROJECT_ID.DATASET_ID`.Logs (Level STRING, Source STRING, Message STRING) AS ( SELECT 'INFO' as Level, '65.177.8.234' as Source, 'Entry Foo-Bar created' as Message UNION ALL SELECT 'WARNING', '132.249.240.10', 'Entry Foo-Bar already exists, created by 65.177.8.234' UNION ALL SELECT 'INFO', '94.60.64.181', 'Entry Foo-Bar deleted' UNION ALL SELECT 'SEVERE', '4.113.82.10', 'Entry Foo-Bar does not exist, deleted by 94.60.64.181' UNION ALL SELECT 'INFO', '181.94.60.64', 'Entry Foo-Baz created' ); -- テーブル全列に検索インデックスを作成 CREATE SEARCH INDEX my_index ON `PROJECT_ID.DATASET_ID`.Logs(ALL COLUMNS);
- 検索インデックスはSEARCH()関数とともに使用することで、テキストの検索が大幅に高速化されます。
- 検索インデックスは、大きなテーブルを想定して設計されています。10 GB 未満のテーブルに検索インデックスを作成した場合、インデックスは入力されません。
- SEARCH(text,query,analyzer)関数のデフォルトのテキスト分析ツールのタイプはLOG_ANALYZER(厳密な一致を求める場合はNO_OP_ANALYZERを指定).
検索インデックスを使用してテキストを検索する
-- テーブル全体から文字列'baz'を含むレコードを抽出 SELECT * FROM `PROJECT_ID.DATASET_ID`.Logs WHERE SEARCH(Logs, 'baz');
search関数では、トークンの区切りに日本語の句読点は含まれないので、日本語テキストの検索は不向き 参考
検索インデックスに関する情報を取得
SELECT table_name, index_name, ddl, index_status FROM `PROJECT_ID.DATASET_ID`.INFORMATION_SCHEMA.SEARCH_INDEXES
テーブルサイズが10G未満の場合、statusはdisabled
検索インデックスを削除する
DROP SEARCH INDEX my_index ON `PROJECT_ID.DATASET_ID`.Logs;
Cloud Storageにクエリを行う
BigLake テーブルを作成する
CREATE EXTERNAL TABLE `PROJECT_ID.DATASET_ID.BIGLAKE_TABLE_ID` WITH CONNECTION `PROJECT_ID.DATASET_ID.CONNECTION_ID` OPTIONS ( format ="CSV", # GCSバケットパス uris = ['BUCKET_PATH'[,...]], );
- 事前に接続の作成必要(bigquery connection APIの認証も必要)
- テーブルをクリックするとコンソール上部にBigLakeマークが表示される
- BigLakeテーブルは行アクセスポリシー編集可能
外部テーブルを作成する
CREATE EXTERNAL TABLE `PROJECT_ID.DATASET_ID.EXTERNAL_TABLE_ID` OPTIONS ( format ="CSV", uris = ['BUCKET_PATH'[,...]] );
- BigLakeテーブルとの違いはサービスアカウントによる接続(WITH CONNECTION)があるかないか
- パーティション分割テーブルに外部テーブルを作成するには、gcs上のファイルがHive パーティション分割レイアウトに従う必要がある
参考:
外部テーブルを作成する
外部でパーティションに分割されたデータを使用する