In Snowflake, custom logic that goes beyond the built-in functions is implemented with UDFs (User Defined Functions) and Stored Procedures. UDFs are used inside SELECT statements to transform or compute values, while Stored Procedures are invoked with CALL and can run complex flows that include DDL and DML.
This article gives a comprehensive walkthrough of the differences between UDFs and Stored Procedures, the CREATE syntax in each supported language, Vectorized UDFs, and the privilege models (Caller's Rights / Owner's Rights). These topics show up frequently in the Data Transformation domain of the SnowPro Core exam.
Here are the differences between UDFs and Stored Procedures at a glance. The SnowPro exam frequently asks which one fits a given situation.
| Aspect | UDF | Stored Procedure |
|---|---|---|
| How to invoke | Called inside SELECT (SELECT my_udf(col)) | Called with CALL (CALL my_proc()) |
| DDL/DML execution | Not allowed (side-effect free) | Allowed (CREATE/INSERT/UPDATE/DELETE) |
| Side effects | None (must be side-effect free) | Allowed (can change DB state) |
| Return value | Scalar value or table (UDTF) | Single scalar value |
| GRANT syntax | GRANT USAGE ON FUNCTION | GRANT USAGE ON PROCEDURE |
| Privilege model | Runs with caller's privileges | Owner's Rights (default) / Caller's Rights |
| Transactions | Runs inside the caller's transaction | Can manage its own transactions |
| Where it can be used | SELECT / WHERE / JOIN / HAVING clauses | CALL only (cannot be used inside SELECT) |
Takes one or more input values and returns a single value. It can be used anywhere — in the SELECT list, WHERE clause, JOIN conditions, and more — for things like custom hash functions or business-rule calculations.
-- SQL scalar UDF: tax-inclusive price calculation
CREATE OR REPLACE FUNCTION calc_tax_price(price NUMBER, tax_rate NUMBER)
RETURNS NUMBER(12,2)
LANGUAGE SQL
AS
$
price * (1 + tax_rate)
$;
-- Example usage
SELECT product_name, calc_tax_price(price, 0.10) AS tax_included
FROM products;A function that returns a tabular result (multiple rows and columns). It is invoked in the FROM clause as TABLE(my_udtf(args)), and is used for unpivoting one input row into many output rows, or for aggregations that span multiple rows.
-- Python UDTF: split a CSV string into rows
CREATE OR REPLACE FUNCTION split_csv(csv_string VARCHAR)
RETURNS TABLE(item VARCHAR)
LANGUAGE PYTHON
RUNTIME_VERSION = '3.11'
HANDLER = 'SplitCSV'
AS
$
class SplitCSV:
def process(self, csv_string):
for item in csv_string.split(','):
yield (item.strip(),)
$;
-- Example usage
SELECT t.item
FROM my_table, TABLE(split_csv(my_table.tags)) t;| Language | Scalar UDF | UDTF | Vectorized UDF | Stored Procedure | Notes |
|---|---|---|---|---|---|
| SQL | Yes | Yes | - | Yes (Snowflake Scripting) | Lightest weight; no external runtime |
| Python | Yes | Yes | Yes | Yes | Snowpark integration; great for ML / data science |
| JavaScript | Yes | Yes | - | Yes | Historically the first language supported |
| Java | Yes | Yes | Yes | Yes | Runs on the JVM; enterprise-friendly |
| Scala | Yes | Yes | Yes | Yes | Runs on the JVM; familiar to Spark users |
Vectorized UDFs are the high-performance variant of Python UDFs. While a regular scalar UDF calls the Python function once per row, a Vectorized UDF receives input as a batch of pandas.Series and uses vectorized operations. Expect 5-50x throughput gains on numeric computation over large row counts.
-- Vectorized UDF: receive pandas.Series and process in batches
CREATE OR REPLACE FUNCTION normalize_score(score NUMBER)
RETURNS NUMBER
LANGUAGE PYTHON
RUNTIME_VERSION = '3.11'
PACKAGES = ('pandas')
HANDLER = 'normalize'
AS
$
import pandas as pd
def normalize(score: pd.Series) -> pd.Series:
return (score - score.mean()) / score.std()
$;With Snowpark, you can define the equivalent Vectorized UDF by setting PandasSeries in theinput_types argument of the @udf decorator.
A Stored Procedure is program logic invoked with CALL. It can implement complex flows that include DDL/DML execution, transaction control, conditional branching, and exception handling.
-- Snowflake Scripting (SQL) Stored Procedure
CREATE OR REPLACE PROCEDURE archive_old_orders(cutoff_date DATE)
RETURNS VARCHAR
LANGUAGE SQL
EXECUTE AS OWNER
AS
$
BEGIN
INSERT INTO orders_archive
SELECT * FROM orders WHERE order_date < :cutoff_date;
DELETE FROM orders WHERE order_date < :cutoff_date;
RETURN 'Archived orders before ' || :cutoff_date;
EXCEPTION
WHEN OTHER THEN
ROLLBACK;
RETURN 'Error: ' || SQLERRM;
END;
$;
-- Run
CALL archive_old_orders('2025-01-01');| Aspect | Owner's Rights (default) | Caller's Rights |
|---|---|---|
| Execution privileges | The Stored Procedure owner's role | The caller's role |
| How to specify | EXECUTE AS OWNER (default) | EXECUTE AS CALLER |
| Access scope | Objects the owner can access | Objects the caller can access |
| Privilege escalation | Possible (runs with broader rights than the caller) | None (stays with the caller's rights) |
| Use cases | Restricted data operations; writing audit logs | Generic utilities; dynamic SQL |
-- Example of Caller's Rights
CREATE OR REPLACE PROCEDURE list_tables(db_name VARCHAR)
RETURNS TABLE(table_name VARCHAR)
LANGUAGE SQL
EXECUTE AS CALLER
AS
$
BEGIN
RETURN TABLE(
SELECT table_name FROM information_schema.tables
WHERE table_catalog = :db_name
);
END;
$;With the Snowpark API, you define functions using the @udf, @udtf, and @sproc decorators and register them in Snowflake through the session.
packages parameterimports parameteris_permanent=True to persist as a permanent UDF/Stored ProcedureUDF vs Stored Procedure
問題 1
Which statement correctly describes how a Snowflake Stored Procedure behaves when created with EXECUTE AS OWNER (the default)?
正解: B
Owner's Rights (EXECUTE AS OWNER) is the default privilege model for Stored Procedures. SQL inside the procedure runs with the owner role's privileges, so it can operate on tables the caller cannot access directly, as long as the owner has the necessary privileges. This lets you allow data operations through controlled business logic without granting regular users direct table access. Caller's Rights (EXECUTE AS CALLER), in contrast, runs with the caller's role and does not allow privilege escalation.
Practice UDF & Stored Procedure questions
Test your SnowPro readiness with our bilingual question bank
Try free questions →What is the biggest difference between a UDF and a Stored Procedure?
The biggest differences are how you call them and whether side effects are allowed. A UDF is called inside a SELECT statement like a built-in function and returns a value. It cannot run DDL/DML and must be side-effect free. A Stored Procedure, in contrast, is invoked with CALL and can run DDL/DML such as CREATE TABLE, INSERT, UPDATE, and DELETE. UDFs always run with the caller's privileges, while Stored Procedures have two rights models — Owner's Rights (default) and Caller's Rights — switchable via the EXECUTE AS CALLER clause. For grants, UDFs use GRANT USAGE ON FUNCTION and Stored Procedures use GRANT USAGE ON PROCEDURE.
When are Vectorized UDFs (Pandas UDFs) most effective?
Vectorized UDFs are most effective for numeric computation and string transformation over large row counts. A regular scalar UDF invokes the Python interpreter for every row, so once you exceed roughly one million rows the function-call overhead dominates. Vectorized UDFs receive input as batches of pandas.Series (a few thousand rows by default) and leverage vectorized NumPy/pandas operations, delivering 5-50x throughput improvements compared to scalar UDFs. You enable them either by specifying the _sf_vectorized_input handler hint in CREATE FUNCTION, or via Snowpark's @udf(input_types=[PandasSeries[int]]) decorator.
When should I use Caller's Rights versus Owner's Rights?
Use Owner's Rights (the default) when you want callers to safely operate on tables they cannot access directly, routing them through the Stored Procedure. For example, you may not want to grant regular users direct UPDATE privileges and instead only allow updates that flow through specific business logic. Caller's Rights (EXECUTE AS CALLER) runs with the caller's privileges and is well suited to generic utility procedures (fetching table metadata, writing logs, etc.). On the security side, Owner's Rights carries a privilege-escalation risk: keep grants on the procedure owner role to a minimum, and verify the caller with CURRENT_ROLE() or IS_ROLE_IN_SESSION().
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
Snowflake Certifications: All 11 Exams Explained (2026)
Every SnowPro certification — Associate, Core, Specialty, Ad...
Snowflake Exam Difficulty Ranking: All 11 Certs Compared (2026)
All 11 SnowPro exams ranked by difficulty with study-time es...
Snowflake Study Guide: Fastest Pass Route by Exam (2026)
How to pass SnowPro certifications efficiently — official ma...
SnowPro Core (COF-C03): Complete Exam Guide (2026)
Pass the SnowPro Core exam — six domains, scope, sample ques...
SnowPro Associate Platform (SOL-C01): Complete Guide (2026)
The entry-level SnowPro Associate exam — scope, weighting, s...