BigQuery
https://gyazo.com/c32baba970bf9c8175cb295eb9043b5f
凄すぎる
API とリファレンス  |  BigQuery  |  Google Cloud
標準 SQL 関数と演算子  |  BigQuery  |  Google Cloud
標準 SQL での分析関数の概要  |  BigQuery  |  Google Cloud
BigQuery のスーパーパワーを引き出す 10 個の重要なヒント | Google Cloud Blog
スキーマの自動検出の使用  |  BigQuery  |  Google Cloud
テク
BigQueryによるデータ分析のための前処理Tips - ZOZO Technologies TECH BLOG
数百GBのデータをMySQLからBigQueryへ同期する - Mercari Engineering Blog
BigQuery で 1 円も溶かさない人の顔 (ZERO BYTE STRUCT を考案した) - Qiita
2023年のBigQuery権限管理 - Google スライド
クエリの書き方テク
こっち 👉 BigQuery クエリ
常識
SELECT * しない
列志向なので、全部なめるのと同じ料金、LIMIT で絞ってもいみはない
SELECT * EXCEPT(hoge)
Cmd+Shift+F を押せ
クエリがフォーマットされる
ゼロ除算
標準 SQL 関数と演算子  |  BigQuery  |  Google Cloud
SAFE_DIVIDE(X, Y) 関数がある、ゼロ除算が発生すると NULL になる
IFNULL(SAFE_DIVIDE(X, Y), 0) などすると、NULL の時は 0 にできる
(ほかにも SAFE_ がいろいろある)
標準SQLとレガシーSQL
CLI はまだデフォルトがレガシー、--use_legacy_sql=false することで標準になる
DISTINCT の精度
GROUP EACH BY
SQL の常識
クエリ構文  |  BigQuery  |  Google Cloud
HAVING は ORDER BY の前
というより ORDER BY, LIMIT はとにかく後ろである
GROUP BY
グループ化可能なデータ型 - データ型  |  BigQuery  |  Google Cloud
重複除去
distinct - Delete duplicate rows from a BigQuery table - Stack Overflow
(やってない)
code:replace-distinct.sql
CREATE OR REPLACE TABLE project.dataset.table
AS SELECT DISTINCT * FROM project.dataset.table
DISTINCT と EXACT_COUNT_DISTINCT
レガシーでは DISTINCT は概算値, 正確な COUNT(DISTINCT(...)) がほしいなら EXACT_COUNT_DISTINCT を使う
標準 SQL では DISTINCT は正確な値、概算でいい場合は APPROX_COUNT_DISTINCT を使う
レガシー SQL では COUNT(DISTINCT x) は概算の数が返されます。標準 SQL では正確な数が返されます。重複しない値のおおよその数を返す APPROX_COUNT_DISTINCT を使用すると、実行時間が短縮され、必要なリソースも少なくて済みます。
標準 SQL への移行  |  BigQuery  |  Google Cloud
DATETIME と TIMESTAMP
標準 SQL データ型  |  BigQuery  |  Google Cloud
DATETIME オブジェクトは、タイムゾーンに依存せずにカレンダーや時計に表示される日時を表します。これには年、月、日、時、分、秒、サブ秒が含まれます。絶対的な時刻を表すには、タイムスタンプを使用します。
TIMESTAMP オブジェクトは、タイムゾーンや夏時間などの慣習に関係なく、マイクロ秒精度の絶対的な時刻を表します。
JOIN した際にカラム名がかぶる
例えば created などのカラムはよく重複する
Duplicate column names in the result are not supported. Found duplicate(s): ...
同名のカラムを出力すると上のようなエラーが出るので、以下のように EXCEPT して解決する。
code:join-except.sql
SELECT
table_a.*,
table_b.* EXCEPT(created, updated),
FROM table_a JOIN table_b ON table_a.hoge_id = table_b.id
ストリーミングバッファにあるデータに影響ある更新系クエリは流せない
UPDATE or DELETE statement over table ... would affect rows in the streaming buffer, which is not supported
Stackdriver Logging → BigQuery の sink でデータを転送しているとき、BigQuery に到着しているが書き込まれる前のデータはストリーミングバッファ上にある
Life of a BigQuery streaming insert | Google Cloud Blog
参照系クエリではストリーミングバッファの内容も読まれる
更新系クエリでストリーミングバッファ上のデータが影響を受ける場合はエラーが出る
ストリーミングバッファは "最も早いエントリ時間" を持ってる
ストリーミングバッファはどのぐらいのタイミングで flush されるのか
もろもろのサイズによるらしい
26MB 50000行/h ぐらいのデータは1時間程度では flush されない、sink 作成直後だからかもしれないが...
テーブルの更新時刻
説明の編集等でも更新される、データが見たいならちゃんと SELECT すること
Firestore export の metadata ファイルは protocol buffer
メタデータ ファイルのエクスポート / エンティティのエクスポートとインポート  |  Cloud Datastore ドキュメント  |  Google Cloud
source: asia-northeast1, destination: asia
BigQuery の export で Multi-Regional な bucket に export したらエラーになる
クラスタ化テーブル
WIP
View の作成の権限
クエリ文字列を入れれたらいいじゃん、というわけではなくて参照先のテーブルにアクセスできる必要がある(内部的にView作成時に参照先テーブルのスキーマを読む, 表示されうるため?)
Terraform でクロスプロジェクトの View を作って管理する、みたいなときに面倒
2022/03/16 のような / 区切りで書かれた日付を load したい
できない
Cloud Storage からの CSV データの読み込み  |  BigQuery  |  Google Cloud
CSV データまたは JSON データを読み込む場合、DATE 列の値に区切りとしてダッシュ(-)を使用し、YYYY-MM-DD(年-月-日)の形式にする必要があります。
一度 STRING としてロードして PARSE_DATE('%Y/%m/%d', string_date) する
DATE でサポートされる形式設定要素 - 日付関数  |  BigQuery  |  Google Cloud
矛盾した Timezone 指定はエラーになる
SELECT TIMESTAMP("2022-01-01T00:00:00+09:00", "America/Los_Angels") はエラーが出てくれるので安心
ロール
事前定義ロールと権限  |  BigQuery  |  Google Cloud
BigQuery FAQ – よくある質問と解説 | クラウドサービス徹底比較・徹底解説 (2021年版)
roles/bigquery.jobUser
プロジェクトに課金させてジョブを実行できる
事前定義ロールと権限  |  BigQuery  |  Google Cloud だと少ないけどもっと色々出来たイメージだが...
? roles/bigquery.user はジョブ実行できるんだっけ
フォーマッタ
Matts966/zetasql-formatter: ZetaSQL Formatter with preserved comments mainly for BigQuery and SpanSQL
BigQueryコーディングスタイル | Queuery
組み込みのやつはツラすぎるのであまり使わない
セッション
セッションの概要  |  BigQuery  |  Google Cloud
対話的に調べたり何個もクエリを実行するときに費用を抑えられる
CREATE TEMP TABLE name AS SELECT ... でデカめのクエリは名前つけておくとよい
コンソールから既存のセッションにアタッチできる?
できなさそう
セッション作りつつ変数にセッションIDを入れたり、クエリでセッションID指定できるとよいのだが
temporary table 作るのが主な気持ちだからだけど、temporary table 一覧がほしい
確実にキャッシュを使う
キャッシュを確実に使用する - キャッシュに保存されているクエリ結果を使用する  |  BigQuery  |  Google Cloud
createDisposition: "CREATE_NEVER" で、キャッシュにないときは失敗する
[* ARRAY_AGG(...)[OFFSET(0)] のほうが効率がいい]
関数のベストプラクティス  |  BigQuery  |  Google Cloud
テーブル定義
カラムを捨てる
テーブル スキーマからの列の削除 - テーブル スキーマの手動変更  |  BigQuery  |  Google Cloud
Struct の中身は EXCEPT できないので、一部を落とすには SELECT * REPLACE する
SELECT 式 - 標準 SQL のクエリ構文  |  BigQuery  |  Google Cloud
sql - BigQuery select * except nested column - Stack Overflow
code:select-replace.sql
SELECT * REPLACE ((SELECT AS STRUCT payload.* EXCEPT (comment)) AS payload)
FROM ...
テーブル定義以外でも、STRUCT 中の特定フィールドを除いて SELECT する際に
(SELECT AS STRUCT payload.* EXCEPT (comment)) AS payload は使える
jsonPayload 以下しか興味ないなら SELECT jsonPayload.* EXCEPT(field) でもいい
カラムの型を変える
テーブル スキーマの手動変更  |  BigQuery  |  Google Cloud
SELECT * EXCEPT (foo), CAST(foo AS INT64) as foo などで特定のカラムだけ型を変更する、元テーブルを宛先テーブルにしたらよい
jsonPayload などネストしたテーブルの場合は SELECT * REPLACE でやる
標準 SQL の式サブクエリ  |  BigQuery  |  Google Cloud
code:select-replace-example.sql
SELECT * REPLACE (
(
SELECT AS STRUCT jsonPayload.* EXCEPT (event_time, logged_time),
# jsonPayload.event_time を FLOAD などから INT64 へ
CAST(jsonPayload.event_time AS INT64) as event_time,
# jsonPayload.logged_time を INT64(epoch) から TIMESTAMP へ
TIMESTAMP_SECONDS(jsonPayload.event_time) as event_time,
) AS jsonPayload
) FROM ...
パーティションを確認
👉BigQuery View とパーティション
その他 > クエリの設定 > SQL 言語 をレガシーにして以下のクエリ
code:partition.sql
SELECT partition_id,creation_time,last_modified_time FROM Dataset.MyTable$__PARTITIONS_SUMMARY__
BigQuery の Partitioned Table 調査記録 - Qiita
CLI はデフォルトで legacy_sql なので CLI からのほうが楽かも
レガシー SQL 関数と演算子  |  BigQuery  |  Google Cloud
code:partition.sh
$ bq query --project_id={project} 'SELECT partition_id, MSEC_TO_TIMESTAMP(creation_time), MSEC_TO_TIMESTAMP(last_modified_time) FROM {dataset}.{table}$__PARTITIONS_SUMMARY__'
2022/10/18 もう INFORMATION_SCHEMA.PARTITIONS がある
PARTITIONS ビュー  |  BigQuery  |  Google Cloud
スキーマの自動検出
スキーマの自動検出の使用  |  BigQuery  |  Google Cloud
BigQuery スキーマの自動検出 & ISO8601
TIMESTAMP パーティション
時間単位の列パーティション分割テーブルの作成と使用  |  BigQuery  |  Google Cloud
Day だけでなく Month, Year も選べるようになった 2020/9 頃?
CREATE TABLE LIKE
SELECT * FROM dataset.table LIMIT 0 で宛先テーブルを設定すればよい
Copy table structure alone in Bigquery - Stack Overflow
code:like-create-table-like.sql
CREATE TABLE mydataset.myclusteredtable
PARTITION BY DATE(timestamp)
CLUSTER BY customer_id
AS SELECT * FROM mydataset.myothertable LIMIT 0
2022/10/18 今は CREATE TABLE LIKE ある
CREATE TABLE LIKE ステートメント - Google 標準 SQL のデータ定義言語(DDL)ステートメント  |  BigQuery  |  Google Cloud
後からパーティションつける
直接はつけれないけど DDL でスパッといけないかな
Correlated subqueries that reference other tables are not supported unless they can be de-correlated, such as by transforming them into an efficient JOIN.
ARRAY の中身を JOIN する時とかに見る
SELECT ARRAY(SELECT AS STRUCT * FROM UNNEST(array) JOIN other USING(foo)) とか
LEFT JOIN の LEFT を消せば通ったりする
CREATE SCHEMA DDL
は、データセットを作るクエリ
GoogleSQL のデータ定義言語(DDL)ステートメント  |  BigQuery  |  Google Cloud
外部テーブルを DDL で作る
CREATE EXTERNAL TABLE ステートメント - Google 標準 SQL のデータ定義言語(DDL)ステートメント  |  BigQuery  |  Google Cloud
code:external_table.sql
CREATE OR REPLACE EXTERNAL TABLE project.dataset.user (
id STRING OPTIONS(description="..."),
username STRING OPTIONS(description="..."),
is_pro BOOL OPTIONS(description="...")
)
OPTIONS (
format="GOOGLE_SHEETS",
uris=["https://docs.google.com/spreadsheets/d/..."],
sheet_range="users!A:C",
skip_leading_rows=1
)
エラーハンドリング
BEGIN...EXCEPTION...END - 手続き型言語  |  BigQuery  |  Google Cloud
@@error.message などエラー出力を参照できる
Dataform で Dataset 作るところなどで見る
code:create_schema.sql
BEGIN
CREATE SCHEMA IF NOT EXISTS ... OPTIONS(location="asia-northeast1");
EXCEPTION WHEN ERROR THEN
IF NOT CONTAINS_SUBSTR(@@error.message, "already exists: dataset") AND
NOT CONTAINS_SUBSTR(@@error.message, "too many dataset metadata update operations")
THEN
RAISE USING MESSAGE = @@error.message;
END IF;
END;
Cannot use INFORMATION_SCHEMA with a hidden dataset
データセットに名前を付ける - データセットの作成  |  BigQuery  |  Google Cloud
_ から始まる名前のデータセットはコンソール上に表示されないなど特別扱いされる
クエリ自体は普通にできる
ただしデータセットの INFORMATION_SCHEMA を参照しようとするとエラーになる
Invalid value: Cannot use INFORMATION_SCHEMA with a hidden dataset: ...
Dataform によるこういうクエリは実行できない
code:hidden_dataset.sql
SELECT ANY_VALUE(table_type)
FROM myproject._hidden_dataset.INFORMATION_SCHEMA.TABLES
WHERE table_name = 'hoge'
変更履歴
変更履歴の操作  |  BigQuery  |  Google Cloud
FROM APPENDS(TABLE <table_name>, <since>, <until>)
同一テーブルの更新ログなので replace table した場合の履歴は得られない
列・行のアクセス制御
BigQuery でポリシータグを使用する際のベスト プラクティス  |  Google Cloud
列レベルのアクセス制御の概要  |  BigQuery  |  Google Cloud
列にポリシータグを付与、ポリシータグへのアクセス権を管理
BigQuery の行レベルのセキュリティの概要  |  Google Cloud
行アクセスポリシーはクエリで条件を書く、"行" というより WHERE 句で制限する感じ
CREATE ROW ACCESS POLICY ... GRANT TO ("group:hoo@pokutuna.com") FILTER USING (Col="Value")
FILTER USING が真のものに限って結果を得られる
Gemini 叩く
ML.GENERATE_TEXT 関数を使用してテキストを生成する  |  BigQuery  |  Google Cloud
Gemini モデルと ML.GENERATE_TEXT 関数を使用してテキストを生成する  |  BigQuery  |  Google Cloud
外部接続 & モデル作る
code:create_model.sql
CREATE OR REPLACE MODEL hogehoge.ml.flash001
REMOTE WITH CONNECTION projects/hogehoge/locations/asia-northeast1/connections/vertexai
OPTIONS (ENDPOINT = 'gemini-1.5-flash-001');
code:generate.sql
SELECT
*,
-- 最初の出力取り出す
ml_generate_text_result'candidates'0'content''parts'0'text' AS generated_text,
FROM
ML.GENERATE_TEXT(
MODEL ml.flash001,
(SELECT "こんにちは" AS prompt)
)
bq コマンド
コマンドライン ツール リファレンス  |  BigQuery  |  Google Cloud
$ bq ls
$ bq mk --location=asia-northeast1 {dataset_name}
$ bq show {dataset_name 他いろいろ}
$ bq show --format=prettyjson {project}:{dataset}.appengine_googleapis_com_stdout スキーマ見る
$ bq load --source_format=NEWLINE_DELIMITED_JSON --autodetect my_dataset.my_table ./path/to/data.json
$ bq --project_id=<PROJECT_ID> load --source_format=NEWLINE_DELIMITED_JSON --autodetect --time_partitioning_field=<FIELD> <DATASET>.<TABLE> gs://...
スキーマの確認
SELECT * FROM {dataset}.INFORMATION_SCHEMA.COLUMN_FIELD_PATHS
WHERE table_name = ... で絞れる
$ bq show --schema --format=prettyjson {dataset}.{table}
データを別のテーブルにコピーする
あるテーブルのデータを別のテーブルに突っ込みたい
カラムが増えたら追加してほしい
コンソールから書き込み先テーブルを指定する場合、同時にスキーマは変更できない
→ CLI でやるのがよい
コマンドライン ツール リファレンス  |  BigQuery  |  Google Cloud
REQUIRED を NULLABLE にしてもいいならさらに --schema_update_option=ALLOW_FIELD_RELAXATION を追加
確認は --dry_run で
code:copy-to-another-table.sql
bq query \
--project_id=<project> \
--destination_table=<dataset>.<to_table> \
--schema_update_option=ALLOW_FIELD_ADDITION \
--use_legacy_sql=false \
--append_table \
--allow_large_results \
'SELECT * FROM <project>.<dataset>.<from_table>'
Firestore のエクスポートデータを timetamp パーティショニングして BigQuery に取り込む - ぽ靴な缶
普通に CLI 使ったらいけた #blogged
FORMAT は DATASTORE_BACKUP です。Cloud Firestore の場合、適切なオプションは Datastore Backup です。Cloud Firestore と Datastore のエクスポート形式は同じです。
https://cloud.google.com/bigquery/docs/loading-data-cloud-firestore?hl=ja
$ bq --location=asia-northeast1 mk --dataset pokutuna-playground:import-firebase-backup
$ bq --location=asia-northeast1 load --source_format=DATASTORE_BACKUP pokutuna-playground:import_firebase_backup.table1 gs://campenguin-firestore-export/2020-01-07T10:11:58_4616/all_namespaces/kind_queries/all_namespaces_kind_queries.export_metadata
調べる
Stackdriver Logging の sink は --schema_update_option= なに?
https://cloud.google.com/bigquery/docs/reference/bq-cli-reference?hl=ja
テーブルのクラスタ
https://cloud.google.com/bigquery/docs/creating-clustered-tables?hl=ja
リージョンを横断した JOIN とかするとどうなっちゃうの?
できない
GCP のカレントプロジェクトのリージョンの影響がある? なんか実行できないことがある気がする
クエリの設定でリージョン変えれば良い?
クラスタ化テーブルの概要  |  BigQuery  |  Google Cloud
書き込み設定
"テーブルに追加する" は bq load --append_table, "テーブルを上書きする" は --replace かな?
bq load / コマンドライン ツール リファレンス  |  BigQuery  |  Google Cloud
https://gyazo.com/2782532f60b08c8093e6ebbfda487a23
取り込み時間分割テーブル
Stackdriver Logging → BigQuery
承認済みビュー
BigQueryと上手に付き合う4つのTips | google-cloud-jp
一部のデータを社外に共有する際などに、承認済みビューを作ってそれを割り当てる
CREATE TABLE LIKE ... みたいなのやりたい
code:select_schema
SELECT STRING_AGG(CONCAT(column_name, ", ", data_type), "\n")
FROM {dataset}.INFORMATION_SCHEMA.COLUMN_FIELD_PATHS
WHERE table_name = {table}
AND field_path NOT LIKE "%.%"
みたいな感じでスキーマ文字列作れそうなので、これを CREATE TABLE に渡せないか
(bq show & bq mk のほうが簡単とはいえ)
node client で結果は使わずジョブの終了を待つ
code:wait.ts
await job.getQueryResults({maxResults: 0});
Spreadsheet への federated query で Drive credentials エラー
Permission denied while getting Drive credentials.
読み取るサービスアカウントは対象のシートへの閲覧権限はあるがうまくいかない
アクセススコープに https://www.googleapis.com/auth/drive がなかった
これ impersonate しているところで出ていた
普通の ADC で、ユーザーの認証なら https://www.googleapis.com/auth/drive のスコープなくても Spreadsheet にクエリできる気がする、どうだっけ?
--enable-gdrive-access でなくても
#GoogleCloud #dev