Home Database Stored Procedures, UDFs, and Snowflake Scripting: A Practical Guide
Intermediate 3 min · July 17, 2026

Stored Procedures, UDFs, and Snowflake Scripting: A Practical Guide

Learn to create and use Snowflake stored procedures, UDFs, and Snowflake Scripting with production-ready examples, debugging tips, and best practices for developers..

N
Naren Founder & Principal Engineer

20+ years shipping high-throughput database systems. Notes here come from systems that actually shipped.

Follow
Production
production tested
July 17, 2026
last updated
2,439
articles · all by Naren
Before you start⏱ 15-20 min read
  • Basic knowledge of SQL (SELECT, INSERT, UPDATE, DELETE).
  • A Snowflake account with access to create procedures and functions.
  • Familiarity with Snowflake's web interface or a SQL client.
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Stored procedures encapsulate business logic with multiple SQL statements and control flow.
  • UDFs (User-Defined Functions) return a single value per call, ideal for reusable calculations.
  • Snowflake Scripting extends SQL with variables, loops, and conditionals using JavaScript or SQL.
  • Use stored procedures for complex ETL, UDFs for row-level transformations.
  • Debug with system functions like SYSTEM$LOG and query history.
✦ Definition~90s read
What is Stored Procedures, UDFs, and Snowflake Scripting?

Snowflake stored procedures and UDFs allow you to write reusable, procedural code that extends Snowflake's SQL capabilities for complex data transformations and automation.

Think of stored procedures as a recipe that tells the kitchen staff (Snowflake) exactly how to prepare a meal step-by-step, including when to preheat the oven and how long to stir.
Plain-English First

Think of stored procedures as a recipe that tells the kitchen staff (Snowflake) exactly how to prepare a meal step-by-step, including when to preheat the oven and how long to stir. UDFs are like a spice blend you can reuse in many recipes—you define it once and sprinkle it wherever needed. Snowflake Scripting is the language of the recipe itself, allowing you to write instructions like 'if the sauce is too thick, add water' or 'repeat the stirring step 5 times.'

⚙ Browser compatibility
Latest versions — ✓ supported
ChromeFirefoxSafariEdge

Imagine you're a data engineer at a fast-growing e-commerce company. Every night, you need to clean raw sales data, calculate customer lifetime value, and load it into a reporting table. Without stored procedures, you'd manually run a dozen SQL statements in sequence, hoping you don't miss a step. With Snowflake stored procedures and UDFs, you can automate this entire pipeline, handle errors gracefully, and reuse logic across multiple workflows.

Snowflake's support for stored procedures and user-defined functions (UDFs) brings the power of procedural programming to your data warehouse. Whether you're migrating from traditional databases like PostgreSQL or building new data pipelines, understanding these features is essential for writing maintainable, efficient code. Snowflake Scripting, introduced in 2021, provides a native SQL-based language for writing stored procedures, reducing the need for JavaScript or Python.

In this tutorial, you'll learn how to create and use stored procedures and UDFs in Snowflake, with a focus on real-world scenarios. We'll cover Snowflake Scripting syntax, debugging techniques, and common pitfalls. By the end, you'll be able to automate complex data transformations, improve code reusability, and troubleshoot production issues like a pro.

1. Introduction to Snowflake Stored Procedures and UDFs

Snowflake stored procedures allow you to write procedural code that executes multiple SQL statements, supports conditional logic (IF/ELSE), loops (FOR, WHILE), and exception handling. They can be written in JavaScript (using Snowflake's JavaScript API) or Snowflake Scripting (a SQL-based procedural language). UDFs, on the other hand, are functions that return a single scalar value or a table. They are ideal for encapsulating reusable calculations or transformations that can be used in SELECT statements.

When should you use each? Use stored procedures for tasks that require multiple steps, such as ETL pipelines, data validation, or administrative operations. Use UDFs for row-level computations, like calculating discounted prices or formatting strings. Both support parameters and can be called from SQL.

Snowflake Scripting is the recommended approach for new development because it integrates seamlessly with SQL and is easier to read and maintain. However, JavaScript procedures offer more flexibility for complex logic and integration with external APIs.

In this section, we'll create a simple stored procedure and a UDF to get started.

basic_procedure_udf.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- Create a stored procedure using Snowflake Scripting
CREATE OR REPLACE PROCEDURE greet_user(user_name VARCHAR)
RETURNS VARCHAR
LANGUAGE SQL
AS
$$
BEGIN
  RETURN 'Hello, ' || user_name || '!';
END;
$$;

-- Call the procedure
CALL greet_user('Alice');

-- Create a scalar UDF
CREATE OR REPLACE FUNCTION add_tax(price NUMBER)
RETURNS NUMBER
AS
$$
  price * 1.08
$$;

-- Use the UDF in a query
SELECT add_tax(100) AS price_with_tax;
Output
Hello, Alice!
108.0
💡Snowflake Scripting vs JavaScript
📊 Production Insight
Always use CREATE OR REPLACE to avoid errors when updating procedures/functions. Version control your code using Git.
🎯 Key Takeaway
Stored procedures and UDFs extend Snowflake's capabilities beyond single SQL statements, enabling reusable and modular code.

2. Snowflake Scripting: Variables, Cursors, and Control Flow

Snowflake Scripting introduces variables, cursors, and control flow constructs that make procedural programming possible. Variables are declared using DECLARE or LET, and can be of any Snowflake data type. Cursors allow you to iterate over query results row by row. Control flow includes IF/ELSE, FOR, WHILE, and REPEAT loops.

Let's build a procedure that processes orders and updates inventory. This example demonstrates variables, a cursor, and a loop.

scripting_example.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CREATE OR REPLACE PROCEDURE process_orders()
RETURNS VARCHAR
LANGUAGE SQL
AS
$$
DECLARE
  order_id INT;
  order_total NUMBER;
  cur CURSOR FOR SELECT id, total FROM orders WHERE status = 'PENDING';
  processed_count INT DEFAULT 0;
BEGIN
  OPEN cur;
  FOR rec IN cur DO
    order_id := rec.id;
    order_total := rec.total;
    -- Update inventory logic (simplified)
    UPDATE inventory SET quantity = quantity - 1 WHERE product_id IN (SELECT product_id FROM order_items WHERE order_id = order_id);
    UPDATE orders SET status = 'PROCESSED' WHERE id = order_id;
    processed_count := processed_count + 1;
  END FOR;
  CLOSE cur;
  RETURN 'Processed ' || processed_count || ' orders.';
END;
$$;

CALL process_orders();
Output
Processed 5 orders.
⚠ Cursor Performance
📊 Production Insight
Always close cursors explicitly to free resources. Use exception handling (EXCEPTION block) to catch errors during cursor operations.
🎯 Key Takeaway
Snowflake Scripting provides familiar procedural constructs, but prefer set-based operations for performance.

3. Creating and Using User-Defined Functions (UDFs)

UDFs in Snowflake can be scalar (return a single value) or tabular (return a table). They can be written in SQL, JavaScript, Python, or Java. SQL UDFs are the simplest and most performant for simple calculations. JavaScript UDFs allow more complex logic, such as string manipulation or date arithmetic.

Here's an example of a SQL UDF that calculates the number of business days between two dates, excluding weekends.

business_days_udf.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
CREATE OR REPLACE FUNCTION business_days(start_date DATE, end_date DATE)
RETURNS INT
AS
$$
  SELECT COUNT(*)::INT
  FROM TABLE(GENERATOR(ROWCOUNT => DATEDIFF('day', start_date, end_date) + 1))
  WHERE DATEADD('day', SEQ4(), start_date) BETWEEN start_date AND end_date
    AND DAYOFWEEK(DATEADD('day', SEQ4(), start_date)) NOT IN (0, 6)
$$;

-- Usage
SELECT business_days('2024-01-01', '2024-01-10') AS business_days;
Output
8
🔥UDF Determinism
📊 Production Insight
Avoid using UDFs in WHERE clauses on large tables because they may prevent predicate pushdown. Instead, compute the value in a subquery or join.
🎯 Key Takeaway
UDFs encapsulate reusable logic and can be used directly in SQL queries, improving code readability and maintainability.

4. Stored Procedures for ETL: A Real-World Example

ETL (Extract, Transform, Load) pipelines are a common use case for stored procedures. Let's build a procedure that extracts raw sales data from a staging table, transforms it by calculating commissions, and loads it into a reporting table. This example uses Snowflake Scripting with error handling and logging.

etl_procedure.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
CREATE OR REPLACE PROCEDURE etl_sales_pipeline()
RETURNS VARCHAR
LANGUAGE SQL
AS
$$
DECLARE
  start_time TIMESTAMP;
  end_time TIMESTAMP;
  rows_affected INT;
BEGIN
  start_time := CURRENT_TIMESTAMP;
  
  -- Step 1: Extract and transform
  CREATE OR REPLACE TEMP TABLE transformed_sales AS
  SELECT 
    sale_id,
    product_id,
    quantity,
    price,
    quantity * price AS revenue,
    CASE 
      WHEN quantity > 10 THEN revenue * 0.1
      ELSE revenue * 0.05
    END AS commission
  FROM raw_sales
  WHERE processed_flag = FALSE;
  
  rows_affected := (SELECT COUNT(*) FROM transformed_sales);
  
  -- Step 2: Load into reporting table
  INSERT INTO sales_report (sale_id, product_id, revenue, commission, processed_date)
  SELECT sale_id, product_id, revenue, commission, CURRENT_TIMESTAMP
  FROM transformed_sales;
  
  -- Step 3: Mark raw data as processed
  UPDATE raw_sales SET processed_flag = TRUE
  WHERE sale_id IN (SELECT sale_id FROM transformed_sales);
  
  end_time := CURRENT_TIMESTAMP;
  
  -- Log the execution
  INSERT INTO etl_log (procedure_name, start_time, end_time, rows_processed, status)
  VALUES ('etl_sales_pipeline', start_time, end_time, rows_affected, 'SUCCESS');
  
  RETURN 'Processed ' || rows_affected || ' rows.';
EXCEPTION
  WHEN OTHER THEN
    INSERT INTO etl_log (procedure_name, start_time, end_time, rows_processed, status, error_message)
    VALUES ('etl_sales_pipeline', start_time, CURRENT_TIMESTAMP, 0, 'FAILED', SQLERRM);
    RETURN 'Error: ' || SQLERRM;
END;
$$;
Output
Processed 1500 rows.
💡Logging Best Practices
📊 Production Insight
Use temporary tables for intermediate results to avoid locking and reduce resource contention. Always handle exceptions to prevent partial updates.
🎯 Key Takeaway
Stored procedures are ideal for multi-step ETL processes, providing transaction control, error handling, and logging.

5. JavaScript Stored Procedures: When and How to Use Them

JavaScript stored procedures offer more flexibility than Snowflake Scripting, especially for complex logic, external API calls, or when you need to use JavaScript libraries. However, they require understanding Snowflake's JavaScript API, which includes methods like snowflake.execute(), snowflake.createStatement(), and resultSet.

Here's an example of a JavaScript procedure that calls an external API (simulated) to enrich customer data.

javascript_procedure.jsJAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CREATE OR REPLACE PROCEDURE enrich_customer_data(customer_id INT)
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
AS
$$
  var result = '';
  try {
    // Simulate external API call (replace with actual HTTP request)
    var apiResponse = '{"name": "John Doe", "email": "john@example.com"}';
    var customer = JSON.parse(apiResponse);
    
    // Update customer table
    var updateStmt = snowflake.createStatement({
      sqlText: 'UPDATE customers SET name = ?, email = ? WHERE id = ?',
      binds: [customer.name, customer.email, customer_id]
    });
    updateStmt.execute();
    
    result = 'Customer ' + customer_id + ' enriched successfully.';
  } catch (err) {
    result = 'Error: ' + err.message;
  }
  return result;
$$;

CALL enrich_customer_data(123);
Output
Customer 123 enriched successfully.
Try it live
⚠ External Network Access
📊 Production Insight
Use JavaScript procedures sparingly; prefer Snowflake Scripting for simpler tasks. Always test JavaScript procedures with various inputs to catch runtime errors.
🎯 Key Takeaway
JavaScript procedures are powerful for complex logic and external integrations but require careful handling of the Snowflake API and error management.

6. Debugging and Monitoring Stored Procedures and UDFs

Debugging stored procedures in Snowflake can be challenging because you cannot step through code interactively. However, you can use several techniques: - Use SYSTEM$LOG to insert log messages into a table. - Query the QUERY_HISTORY view to see the SQL statements executed by the procedure. - Use the GET_DDL function to review the procedure definition. - For JavaScript procedures, use console.log() (output appears in the query profile).

Here's an example of logging within a Snowflake Scripting procedure.

debugging_procedure.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-- Create a log table
CREATE OR REPLACE TABLE proc_log (
  log_time TIMESTAMP,
  message VARCHAR
);

-- Procedure with logging
CREATE OR REPLACE PROCEDURE debug_example()
RETURNS VARCHAR
LANGUAGE SQL
AS
$$
BEGIN
  INSERT INTO proc_log VALUES (CURRENT_TIMESTAMP, 'Starting procedure');
  
  -- Some logic
  LET x INT := 10;
  INSERT INTO proc_log VALUES (CURRENT_TIMESTAMP, 'Variable x = ' || x);
  
  -- Simulate work
  CREATE OR REPLACE TEMP TABLE temp AS SELECT * FROM orders LIMIT 100;
  INSERT INTO proc_log VALUES (CURRENT_TIMESTAMP, 'Temp table created');
  
  RETURN 'Done';
END;
$$;

CALL debug_example();
SELECT * FROM proc_log ORDER BY log_time;
Output
2024-01-15 10:00:00 | Starting procedure
2024-01-15 10:00:00 | Variable x = 10
2024-01-15 10:00:01 | Temp table created
🔥Query History for Procedures
📊 Production Insight
Remove excessive logging in production to avoid performance overhead. Use conditional logging based on a debug flag parameter.
🎯 Key Takeaway
Logging and query history are essential for debugging stored procedures. Always include logging in production procedures.

7. Best Practices and Performance Considerations

When writing stored procedures and UDFs, follow these best practices: - Use set-based operations instead of cursors whenever possible. - Limit the number of DDL statements inside procedures; they consume more resources. - Use temporary tables for intermediate results to avoid locking. - Mark UDFs as IMMUTABLE or STABLE to enable caching. - Avoid using UDFs in WHERE clauses on large tables. - Use exception handling to catch errors and rollback if necessary. - Monitor procedure execution with Snowflake's QUERY_HISTORY and ACCOUNT_USAGE views.

Performance considerations
  • Stored procedures run in a single session; long-running procedures may hit timeouts.
  • Use the STATEMENT_TIMEOUT_IN_SECONDS parameter to control timeouts.
  • For large data volumes, consider using Snowflake's tasks and streams instead of procedures.
  • UDFs that are used in many rows can be slow; consider using built-in functions or rewriting as joins.
best_practices.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- Example: Set-based update instead of cursor
-- Instead of looping, use a single UPDATE with JOIN
UPDATE inventory i
SET quantity = i.quantity - oi.quantity
FROM order_items oi
WHERE i.product_id = oi.product_id
  AND oi.order_id IN (SELECT id FROM orders WHERE status = 'PENDING');

-- Mark UDF as IMMUTABLE for caching
CREATE OR REPLACE FUNCTION add_tax(price NUMBER)
RETURNS NUMBER
IMMUTABLE
AS
$$
  price * 1.08
$$;
💡Use Tasks for Scheduling
📊 Production Insight
Always test procedures with production-scale data in a non-production environment. Use Snowflake's zero-copy cloning to create a test environment.
🎯 Key Takeaway
Follow best practices to ensure your procedures and UDFs are performant, maintainable, and reliable in production.
● Production incidentPOST-MORTEMseverity: high

The Midnight ETL Failure: A Stored Procedure Timeout

Symptom
The nightly ETL job failed with 'Statement reached its statement timeout' error, leaving the reporting table empty.
Assumption
The developer assumed the procedure would complete within the default 5-minute timeout because it ran fine on a small dataset.
Root cause
The procedure processed millions of rows without explicit transaction commits, causing long-running transactions that hit Snowflake's statement timeout (default 5 minutes).
Fix
Added explicit COMMIT statements after each major batch operation and increased the timeout using the STATEMENT_TIMEOUT_IN_SECONDS parameter.
Key lesson
  • Always test stored procedures with production-scale data volumes.
  • Use explicit transaction control (COMMIT) to break long-running operations into smaller chunks.
  • Set appropriate timeout values using Snowflake parameters like STATEMENT_TIMEOUT_IN_SECONDS.
  • Monitor procedure execution with SYSTEM$CURRENT_USER_TASK_NAME and query history.
  • Implement logging within procedures to track progress and catch failures early.
Production debug guideSymptom to Action5 entries
Symptom · 01
Procedure fails with 'Statement reached its statement timeout'
Fix
Check the procedure's transaction handling. Add COMMIT statements after large operations. Increase STATEMENT_TIMEOUT_IN_SECONDS at session or account level.
Symptom · 02
Procedure returns unexpected results or no rows
Fix
Use SYSTEM$LOG to insert debug messages into a log table. Verify input parameters and intermediate results by temporarily selecting into a table.
Symptom · 03
Procedure runs slowly or consumes excessive credits
Fix
Examine the query profile for the procedure's statements. Look for full table scans, missing filters, or inefficient joins. Consider using temporary tables to stage data.
Symptom · 04
Error: 'Unsupported subquery type' in a UDF
Fix
UDFs cannot contain subqueries that return multiple rows. Rewrite the UDF to use scalar subqueries or pass aggregated values as parameters.
Symptom · 05
Procedure fails with 'JavaScript compilation error'
Fix
Check for syntax errors in JavaScript code. Use console.log for debugging, but remove before production. Ensure all variables are declared with 'var'.
★ Quick Debug Cheat Sheet for Snowflake Stored ProceduresCommon symptoms and immediate actions for debugging stored procedures and UDFs.
Timeout error
Immediate action
Add COMMIT after each batch
Commands
ALTER SESSION SET STATEMENT_TIMEOUT_IN_SECONDS = 3600;
CALL MY_PROCEDURE();
Fix now
Increase timeout and commit frequently.
Unexpected results+
Immediate action
Log intermediate values
Commands
CREATE OR REPLACE TEMP TABLE debug_log (ts TIMESTAMP, msg VARCHAR);
INSERT INTO debug_log SELECT CURRENT_TIMESTAMP, 'Step 1 done';
Fix now
Insert debug messages into a log table.
Slow performance+
Immediate action
Check query profile
Commands
SELECT * FROM TABLE(INFORMATION_SCHEMA.QUERY_HISTORY()) WHERE QUERY_TEXT LIKE '%MY_PROCEDURE%' ORDER BY START_TIME DESC;
EXPLAIN USING JSON SELECT ...;
Fix now
Optimize the slowest query inside the procedure.
UDF subquery error+
Immediate action
Rewrite UDF to avoid multi-row subquery
Commands
CREATE OR REPLACE FUNCTION my_udf(val NUMBER) RETURNS NUMBER AS $$ SELECT MAX(col) FROM table WHERE key = val $$;
CALL my_udf(10);
Fix now
Use scalar subquery or pass aggregated value.
FeatureStored ProcedureUDF
PurposeMulti-step operations, ETL, automationReusable calculation, transformation
Return TypeScalar (single value) or tableScalar or table
Can Modify Data?YesNo
Can Use DDL?YesNo
Used In SQL?Called via CALLUsed in SELECT, WHERE, etc.
LanguagesSQL, JavaScript, Python, JavaSQL, JavaScript, Python, Java
PerformanceSlower due to overheadFast, can be cached
⚙ Quick Reference
7 commands from this guide
FileCommand / CodePurpose
basic_procedure_udf.sqlCREATE OR REPLACE PROCEDURE greet_user(user_name VARCHAR)1. Introduction to Snowflake Stored Procedures and UDFs
scripting_example.sqlCREATE OR REPLACE PROCEDURE process_orders()2. Snowflake Scripting
business_days_udf.sqlCREATE OR REPLACE FUNCTION business_days(start_date DATE, end_date DATE)3. Creating and Using User-Defined Functions (UDFs)
etl_procedure.sqlCREATE OR REPLACE PROCEDURE etl_sales_pipeline()4. Stored Procedures for ETL
javascript_procedure.jsCREATE OR REPLACE PROCEDURE enrich_customer_data(customer_id INT)5. JavaScript Stored Procedures
debugging_procedure.sqlCREATE OR REPLACE TABLE proc_log (6. Debugging and Monitoring Stored Procedures and UDFs
best_practices.sqlUPDATE inventory i7. Best Practices and Performance Considerations

Key takeaways

1
Stored procedures and UDFs enable reusable, modular code in Snowflake, with Snowflake Scripting being the preferred language for most tasks.
2
Use set-based operations over cursors for better performance; only use cursors when row-by-row logic is unavoidable.
3
Always include error handling and logging in production procedures to facilitate debugging and monitoring.
4
Mark UDFs with appropriate volatility to enable caching and improve performance.
5
Test procedures with production-scale data and use Snowflake's query history to identify bottlenecks.

Common mistakes to avoid

5 patterns
×

Not using COMMIT in long-running procedures, causing timeouts.

×

Using cursors for large datasets instead of set-based operations.

×

Creating UDFs without specifying volatility (IMMUTABLE/STABLE/VOLATILE).

×

Using UDFs in WHERE clauses on large tables.

×

Not handling exceptions in stored procedures.

INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
Explain the difference between a stored procedure and a UDF in Snowflake...
Q02SENIOR
How do you handle errors in a Snowflake Scripting stored procedure?
Q03SENIOR
Write a Snowflake Scripting procedure that loops through a cursor and up...
Q04SENIOR
How can you make a UDF more performant in Snowflake?
Q05SENIOR
What is the difference between Snowflake Scripting and JavaScript stored...
Q01 of 05JUNIOR

Explain the difference between a stored procedure and a UDF in Snowflake.

ANSWER
A stored procedure can perform multiple operations, modify data, and use control flow. A UDF returns a single value or table and is used in SQL expressions; it cannot modify data or execute DDL.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between a stored procedure and a UDF in Snowflake?
02
Can I call a stored procedure from within a UDF?
03
How do I pass an array or table to a stored procedure?
04
What languages can I use for Snowflake stored procedures?
05
How do I debug a stored procedure that fails silently?
N
Naren Founder & Principal Engineer

20+ years shipping high-throughput database systems. Notes here come from systems that actually shipped.

Follow
Verified
production tested
July 17, 2026
last updated
2,439
articles · all by Naren
🔥

That's Snowflake. Mark it forged?

3 min read · try the examples if you haven't

Previous
Snowpipe and Snowpipe Streaming: Automated Continuous Data Ingestion
15 / 33 · Snowflake
Next
Materialized Views, Clustering, and Automatic Table Maintenance