トップ / 基礎知識
Snowflake上でカスタムロジックを実行する4つの手段を、入出力の形とSQLからの呼び出し方で整理します。
名前が似ていて混同しやすいですが、判断基準は1つです。1行の入力に対して、何を返すか。
1. スカラUDF:1行 → 1つの値 2. Vectorized UDF:複数行のバッチ → バッチ分の値 3. UDTF:1行 → 0行以上の複数行 4. ストアドプロシージャ(SPROC):表の形にとらわれない任意の処理
スカラUDFは行ごとに呼び出され、1つの値を返す関数です。SELECT句やWHERE句などのSQL式の中から直接呼び出せます。
from snowflake.snowpark.functions import udf
from snowflake.snowpark.types import IntegerType
@udf(name="celsius_to_fahrenheit", return_type=IntegerType(), input_types=[IntegerType()])
def celsius_to_fahrenheit(celsius: int) -> int:
return celsius * 9 // 5 + 32
Python UDFの場合、1行ずつPythonの関数を呼び出すため、行数が多いとPythonの呼び出しオーバーヘッドが積み重なります。
Vectorized UDF(バッチUDF)は、複数行をPandasのSeriesやDataFrameとしてまとめて受け取り、まとめて結果を返す仕組みです。
import pandas as pd
from snowflake.snowpark.functions import udf
from snowflake.snowpark.types import PandasSeriesType, IntegerType
@udf(
name="batch_double",
input_types=[PandasSeriesType(IntegerType())],
return_type=PandasSeriesType(IntegerType()),
max_batch_size=1000,
)
def batch_double(values: pd.Series) -> pd.Series:
return values * 2
Snowflake公式ドキュメントは「Vectorized Python UDFは、入力行のバッチをPandas DataFrameとして受け取り、結果のバッチをPandas配列またはSeriesとして返すPython関数を定義できる」と説明しています。1行ずつPythonを呼び出すオーバーヘッドを避けられるため、機械学習の推論のようにバッチ処理に向く場面で特に有効です。
UDTF(ユーザー定義テーブル関数)は、入力に対して0行以上の複数行を出力できる関数です。公式ドキュメントによれば、UDTFのハンドラクラスは行ごとに呼び出されるprocessメソッドを実装し、そこでタプルとして表形式の値をyieldします。
from snowflake.snowpark.functions import udtf
from snowflake.snowpark.types import StructType, StructField, StringType
@udtf(output_schema=StructType([StructField("word", StringType())]))
class SplitWords:
def process(self, sentence: str):
for word in sentence.split():
yield (word,)
SQLのFROM句から呼び出す点がスカラUDFと異なり、1行の文章から複数の単語行への展開のような「表を膨らませる」処理に使います。
ストアドプロシージャ(SPROC)は、条件分岐・ループ・トランザクション管理・DDL/DMLの実行といった「処理の手順そのもの」をパッケージ化したものです。SQL式の中から呼び出すのではなく、CALL文で独立して実行します。
from snowflake.snowpark.functions import sproc
@sproc(name="archive_old_rows", is_permanent=True, stage_location="@my_stage", replace=True)
def archive_old_rows(session, cutoff_date: str) -> str:
session.sql(f"INSERT INTO archive SELECT * FROM orders WHERE order_date < '{cutoff_date}'").collect()
session.sql(f"DELETE FROM orders WHERE order_date < '{cutoff_date}'").collect()
return "archived"
UDF・UDTFにはできないDDL/DMLの実行が可能な点が大きな違いです。
| 種類 | 主な目的 | 戻り値 | SQL式内から直接呼べるか | DDL/DMLの実行 |
|---|---|---|---|---|
| スカラUDF | 行ごとの計算・変換 | 単一の値 | 可能 | 不可 |
| Vectorized UDF | バッチ単位の高速演算 | 単一の値(バッチ処理) | 可能 | 不可 |
| UDTF | 表構造への展開・変換 | 複数行・複数列 | 可能(FROM句内) | 不可 |
| ストアドプロシージャ | パイプライン制御・自動化 | 任意 | 不可(CALL文) | 可能 |
Polarsはローカルで動くライブラリなので、この4分類そのものが存在しません。Polarsで「カスタムロジック」というと、map_elementsのようなメソッドにPython関数を渡す形が中心で、SQLエンジン側にプッシュダウンして実行させるという概念がそもそもありません。
公式ドキュメントの記載にもとづく解説です(実行検証はしていません)。確認日:2026-08-27