BigQuery のテーブル関数(TVF)で引数にテーブルを渡せるようになりました
データ事業本部のはんざわです。
本日のアップデートにより、BigQuery のテーブル関数(TVF)でテーブルを引数に渡す機能がプレビューとして利用可能になりました。
You can now include table parameters when you create a table-valued function (TVF). This feature is in Preview.
テーブル関数自体は以前から利用可能でしたが、これまではテーブル名を引数として渡すことはできませんでした。
本ブログでは、さっそくこの新機能を実際に試してみたいと思います。
触ってみる
それでは、さっそく新しく追加された機能を試してみます。
今回は orders
という名前のテーブルを作成し、以下の 3 つのカラムを持たせます。
- customer_id(顧客ID)
- order_date(注文日)
- amount(注文金額)
以下の SQL で、サンプルデータを含むテーブルを作成します。
CREATE OR REPLACE TABLE tvf.orders AS
SELECT * FROM UNNEST([
STRUCT(1 AS customer_id, DATE '2024-06-01' AS order_date, 100 AS amount),
STRUCT(1, DATE '2024-06-03', 150),
STRUCT(2, DATE '2024-06-02', 200),
STRUCT(2, DATE '2024-06-03', 300),
STRUCT(3, DATE '2024-06-02', 50)
]);
次に、テーブル関数を定義します。
今回のアップデートのポイントは以下の部分です。
orders_table TABLE<customer_id INT64, amount INT64, order_date DATE>
このように、テーブル型の引数(TABLE
)を関数定義に含めることができるようになりました。
以下は、指定された日付の注文を集計するテーブル関数の定義です。
CREATE OR REPLACE TABLE FUNCTION tvf.get_order_summary (
orders_table TABLE<customer_id INT64, amount INT64, order_date DATE>, target_date DATE
)
RETURNS TABLE<order_date DATE, customer_id INT64, total_orders INT64, total_amount INT64>
AS
SELECT
order_date,
customer_id,
COUNT(*) AS total_orders,
SUM(amount) AS total_amount
FROM
orders_table
WHERE
order_date = target_date
GROUP BY order_date, customer_id;
この関数は、引数として渡されたテーブル(orders_table
)から、指定された日付(target_date
)に該当する注文を集計し、顧客ごとの注文数と合計金額を返します。
それでは、作成したテーブル関数を実際に呼び出してみましょう。
SELECT
*
FROM
tvf.get_order_summary(TABLE tvf.orders, '2024-06-02')
このクエリでは、事前に作成したサンプルテーブルを TABLE
パラメータで渡し、2024年6月2日のデータを取得しています。
期待通り、指定した日付に該当する注文が顧客ごとに集計されていることが確認できました。
カラム名が異なる場合の対応
テーブル関数にテーブルを引数として渡せるようになったことで、これまでよりも汎用性が高いクエリをテーブル関数として登録し、再利用できるようになりました。
その一方で、テーブル関数内で定義されたカラム名と実際に渡すテーブルのカラム名が一致しない場合、どのように対応すればよいのかが気になったので実際に試してみました。
カラム名が異なるテーブルを用意する
まず、元の orders
テーブルとは異なるカラム名を持つ orders_v2
テーブルを作成します。
以下のように、カラム名を変更しました。
- customer_id -> user_id
- order_date -> ordered_at
- amount -> price
CREATE OR REPLACE TABLE tvf.orders_v2 AS
SELECT * FROM UNNEST([
STRUCT(1 AS user_id, DATE '2024-06-01' AS ordered_at, 100 AS price),
STRUCT(1, DATE '2024-06-03', 150),
STRUCT(2, DATE '2024-06-02', 200),
STRUCT(2, DATE '2024-06-03', 300),
STRUCT(3, DATE '2024-06-02', 50)
]);
そのまま呼び出してみる
まずは、そのまま先ほど作成した tvf.get_order_summary
関数を呼び出してみます。
SELECT
*
FROM
tvf.get_order_summary(TABLE tvf.orders_v2, '2024-06-02')
すると、テーブル関数の定義に渡したテーブル(tvf.orders_v2)のカラムが存在しないため次のようなエラーが発生します。
Required column "customer_id" not found in table passed as argument 1 of tvf.get_order_summary(TABLE<customer_id INT64, amount INT64, order_date DATE>, DATE) at [4:25]
CTE でカラム名をマッピングする
このような場合は、次のように CTE(WITH句)を使ってカラム名を一致させてからテーブル関数に渡すことで対応できます。
WITH orders_v2_renamed AS (
SELECT
user_id AS customer_id,
ordered_at AS order_date,
price AS amount
FROM
tvf.orders_v2
)
SELECT
*
FROM
tvf.get_order_summary(TABLE orders_v2_renamed, '2024-06-02')
元の orders
テーブルを使ったときと同様の結果が得られました!
サブクエリでカラム名をマッピングする
また、サブクエリを使ってカラム名を一致させた上で、テーブル関数に直接渡すことも可能です。
以下のように、サブクエリの中でカラム名をリネームし、その結果を関数の TABLE
引数として渡します。
SELECT
*
FROM
tvf.get_order_summary(
(
SELECT
user_id AS customer_id,
ordered_at AS order_date,
price AS amount
FROM
tvf.orders_v2
),
'2024-06-02'
)
CTE を使った場合と同様に、期待通りの結果が得られました!
まとめ
今回は、BigQuery のテーブル関数(TVF)にテーブル名を引数として渡す新機能を試してみました。
このアップデートにより、これまでよりも柔軟かつ汎用的なクエリをテーブル関数として定義・再利用できるようになりました。
- ビューではパラメータを渡せず、柔軟な条件変更に対応しづらい
- UDF(ユーザー定義関数)や UDAF(ユーザー定義集約関数)はスカラー値しか返せないため、複数行・複数列を返すようなロジックには不向き
- 同じ集計処理やフィルタ処理を異なるテーブルにも適用したい
こうした場面で非常に有力な選択肢になると思いますので、ぜひ試してみてください!