概要
BigQueryのユーザ定義関数は呼び出し結果が再利用されるよーって話です。
再利用させないためにはカラムの値を引数として与えよう。
BigQueryのユーザ定義関数
BigQueryのユーザ定義関数を使って、呼び出す度に乱数を返す処理を作成して結果のカラムに追加してみます。
BigQueryでは標準で乱数を返す関数RAND()
が用意されていますが今回はあえてJavaScriptで書きます。
ユーザ定義関数の書き方は
ユーザー定義の関数 | BigQuery | Google Cloud
を参照
引数を与えない場合
JavaScriptの乱数生成処理Math.random()
にはseed値の入力ができないため、乱数を生成するための引数は不要です。
なので、呼び出すだけで乱数を返す関数を作ります。
Query
CREATE TEMPORARY FUNCTION random()
RETURNS STRING
LANGUAGE js AS """
return Math.random().toString();
""";
WITH numbers AS
(
SELECT 1 AS x
UNION ALL
SELECT 2 AS x
UNION ALL
SELECT 3 as x
UNION ALL
SELECT 4 AS x
UNION ALL
SELECT 5 AS x
UNION ALL
SELECT 1 as x
)
SELECT x, random() as random
FROM numbers;
結果
全て同じ値が帰ってきます。
引数を与える場合
ユーザ定義関数内で特に使い道はありませんが、カラムの値を引数として渡してみます。
Query
CREATE TEMPORARY FUNCTION random(x FLOAT64)
RETURNS STRING
LANGUAGE js AS """
return Math.random().toString();
""";
WITH numbers AS
(
SELECT 1 AS x
UNION ALL
SELECT 2 AS x
UNION ALL
SELECT 3 as x
UNION ALL
SELECT 4 AS x
UNION ALL
SELECT 5 AS x
UNION ALL
SELECT 1 as x
)
SELECT x, random(x) as random
FROM numbers;
結果
すべて違う結果になりました。
また、xが1の項目が2つありますが、それぞれ違う値が帰ってきているところを見ると、カラムを引数と取る場合には値が同じでも毎回呼び出しているようです。
というか、処理中に値まで参照して判定していない…?
引数に定数を与えてみる
引数として全ての呼び出しに1を与えてみると…
Query
CREATE TEMPORARY FUNCTION random(x FLOAT64)
RETURNS STRING
LANGUAGE js AS """
return Math.random().toString();
""";
WITH numbers AS
(
SELECT 1 AS x
UNION ALL
SELECT 2 AS x
UNION ALL
SELECT 3 as x
UNION ALL
SELECT 4 AS x
UNION ALL
SELECT 5 AS x
UNION ALL
SELECT 1 as x
)
SELECT x, random(1) as random
FROM numbers;
結果
結果は同じになります。
まとめ
BigQueryにユーザ定義関数を作成した場合の呼び出しについて、
- 引数を渡さない場合、引数に同じ数値を固定値として渡す場合には呼び出し結果は再利用される
- カラムの値を引数として渡した場合、毎回ユーザ定義関数を呼び出すことが出来る
- カラムの値を引数として渡した場合、中の値が同じでも結果は再利用されずにもう一度呼び出される
処理速度を上げるために必要な最適化処理だと思いますが、
知らなくてちょっと悩みました。
で、コレが何の役に立つのかというと、
例えば、全てのログに対してUUIDを発行したい場合などに、引数を渡さずにUUIDを生成した場合に全て同じUUIDを返すという悲しい事が発生します。
「テストするから分かるだろう」って話ですが、
じゃぁ、前任者から引き継いだ関数に内部で使っていない引数があって…
なんてパターンがありそうだなと思いました。
再利用させたくない場合はカラムの値を引数にとりましょう。