CertSafari
    Snowflake SnowPro Advanced: Data Scientist (DSA-C03)· Lessons

    Domain 3 · Lesson 11/16

    Snowflake Data Science Pipelines: Dynamic Tables, Python UDFs, UDTFs and Stored Procedures

    Train a data science model.

    10 min read
    6.2% of exam
    5 sources
    Published 5 Oct 2026
    Docs as of 4 Oct 2026

    What you will be able to do

    • Decide whether a transformation step belongs in a dynamic table, a Python UDF, a Python UDTF or a Python stored procedure
    • Explain how dynamic tables keep feature tables fresh, and what they cannot contain
    • Write the handler shape of a scalar Python UDF and of a partition-aware Python UDTF
    • Explain how Python stored procedures run and schedule pipeline steps, and what the Anaconda terms change

    Key concept

    Pick the pipeline object by the shape of the work — If a step can be written as a SQL SELECT, make it a dynamic table and Snowflake refreshes it for you. Use code objects only when SQL is not enough: a UDF returns one value per row, a UDTF returns rows and can keep state for each partition, and a stored procedure runs multi-step code that can issue its own queries.

    1.Automating data transformation with dynamic tables

    A model only stays as good as the features it is trained on. The first job of a data science pipeline is to keep those features clean and up to date without someone rerunning scripts by hand. In Snowflake the declarative way to do this is a dynamic table. You write a SELECT and set a target lag. Snowflake then works out which base tables the query reads, watches them for changes, and refreshes the result for you.

    A dynamic table that cleans raw orders and derives a line_total feature, refreshed incrementally within 10 minutes of the sourcesql
    CREATE OR ALTER DYNAMIC TABLE dt_orders
        TARGET_LAG = '10 minutes'
        WAREHOUSE = transform_wh
        REFRESH_MODE = INCREMENTAL
    AS
        SELECT
            order_id,
            customer_id,
            order_date,
            TRIM(UPPER(product_name)) AS product_name,
            quantity,
            unit_price,
            quantity * unit_price AS line_total,
            order_status
        FROM raw_orders
        WHERE order_status != 'returned';

    You build a multi-step pipeline by pointing one dynamic table at another, for example a daily aggregate on top of dt_orders. You never declare the order. Snowflake infers the dependency graph from your queries and refreshes the tables in that order against a consistent snapshot, and each refresh is applied atomically. On intermediate tables, TARGET_LAG = DOWNSTREAM means they refresh only when a table downstream needs fresh data. The refresh mode sets how much work each refresh does:

    Dynamic table refresh modes
    REFRESH_MODEBehaviour
    INCREMENTALProcesses only the rows that changed since the last refresh
    FULLRefreshes the entire result set
    AUTOSnowflake chooses at creation time, based on whether the definition supports incremental refresh
    ADAPTIVEIncremental by default; reinitializes automatically when large upstream changes are detected
    CUSTOM_INCREMENTALYou define your own refresh logic with DML statements

    These limits mark where the declarative part of the pipeline ends. Dynamic tables are not suited to workloads that need data fresher than 60 seconds or refresh timing that is strictly guaranteed. They cannot contain stored procedures or external functions. They also cannot use UDTFs outside of lateral joins. Training itself therefore never happens inside a dynamic table: the dynamic table prepares features, and code objects take over from there. Costs come from three places: the warehouse that runs each refresh, Cloud Services for scheduling and dependency tracking (a shorter lag means more scheduling work), and storage.

    Checkpoint 1 of 4· Check yourself

    A feature pipeline is built from three dynamic tables that read from each other. What must the engineer do so that they refresh in the right order?

    Sources1

    2.Python UDFs: one value per row

    Some feature logic, or scoring a trained model, is easier to write in Python than in SQL. A Python UDF puts that logic in a function you can call from any SELECT. It is a scalar function: each call receives one row's arguments and returns exactly one value. Arguments are bound by position, not by name, and the HANDLER clause tells Snowflake which Python function to run. The UDF name does not have to match the handler name.

    The code can be written inline in the AS $$ ... $$ body, or uploaded to a stage and referenced through IMPORTS, with the handler given as module.function. If a handler should work on batches rather than single rows, a vectorized Python UDF receives input as pandas DataFrames and returns pandas arrays or Series. UDFs are also where many training pipelines end: a model trained elsewhere in Snowflake can be uploaded to a stage and then used to create UDFs that perform inference.

    Checkpoint 2 of 4· Fill the gap

    Which value completes the HANDLER clause so this inline UDF runs?

    CREATE OR REPLACE FUNCTION addone(i INT)
      RETURNS INT
      LANGUAGE PYTHON
      RUNTIME_VERSION = '3.12'
      HANDLER = ' ? '
    AS $$
    def addone_py(i):
     return i+1
    $$;

    Sources23

    3.Python UDTFs: rows out, state per partition

    A UDF has to return a single value. When a step needs to return rows, or needs to see a whole group of rows before it answers, the tool is a Python UDTF. Its handler is a class, and Snowflake calls up to three methods on it:

    Methods of a Python UDTF handler class
    MethodRequirementWhat it does
    __init__OptionalRuns once per partition, before process; takes only self and cannot produce output rows
    processRequiredRuns for each input row and yields (or returns) tuples in RETURNS TABLE column order
    end_partitionOptionalFinalizes the partition, returning a tabular value as tuples
    A partition-aware UDTF: process emits one row per input row, end_partition emits a total for the partitionsql
    CREATE OR REPLACE FUNCTION stock_sale_sum(symbol VARCHAR, quantity NUMBER, price NUMBER(10,2))
      RETURNS TABLE (symbol VARCHAR, total NUMBER(10,2))
      LANGUAGE PYTHON
      RUNTIME_VERSION = 3.12
      HANDLER = 'StockSaleSum'
    AS $$
    class StockSaleSum:
        def __init__(self):
            self._cost_total = 0
            self._symbol = ""
    
        def process(self, symbol, quantity, price):
          self._symbol = symbol
          cost = quantity * price
          self._cost_total += cost
          yield (symbol, cost)
    
        def end_partition(self):
          yield (self._symbol, self._cost_total)
    $$;
    The caller decides the partitions with OVER (PARTITION BY ...)sql
    SELECT stock_sale_sum.symbol, total
      FROM stocks_table, TABLE(stock_sale_sum(symbol, quantity, price) OVER (PARTITION BY symbol));

    The partitions are set by the query, not by the function. With __init__ and end_partition the handler is partition-aware. Leave both out and it runs statelessly, row by row. Prefer yield with one statement per output row, because lazy evaluation is more efficient and can help avoid timeouts. A single-value tuple needs a trailing comma, as in yield (cost,). If any handler method raises an exception, processing stops and the calling query fails. UDTFs are limited to 500 input arguments and 500 output columns.

    Checkpoint 3 of 4· Check yourself

    A UDTF has to load a large reference object before it handles the rows of each partition, and must not reload it for every row. Where should that code go?

    Sources4

    4.Python stored procedures: the orchestrating step

    UDFs and UDTFs run inside a query. A Python stored procedure runs on its own and can issue queries itself. Its handler uses the Snowpark API to query, update and transform tables, with a Snowflake warehouse doing the compute. That makes it the natural home for steps that a single SELECT cannot express. To run it on a schedule, you call it from a task. You can also call it directly from SQL.

    Libraries such as scikit-learn come in through the PACKAGES clause. Snowflake recommends using Artifact Repository to import packages from PyPI. Anaconda packages are also available, but first someone must accept the External Offerings Terms, using the ORGADMIN role, once per account. Without that acceptance, stored procedures still run, but you cannot use third-party Anaconda packages, you cannot pin a Snowpark version, and you cannot call to_pandas on a DataFrame. That last limitation stops most training code. To see which package versions are available, query information_schema.packages.

    Checkpoint 4 of 4· Match them up

    Match each pipeline object to the work it fits

    Tap a term, then the definition that fits it.

    Sources5

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.A dynamic table can call the training stored procedure (or an external scoring function) so that the model retrains automatically on every refresh.Why is that wrong?

      Dynamic tables do not support stored procedures or external functions in their definition. Use them to prepare features, and run training in a stored procedure, scheduled with a task if needed.

      Covered in Automating data transformation with dynamic tables

    2. 2.A Python stored procedure can always use DataFrame.to_pandas(); the Anaconda terms only matter for extra libraries.Why is that wrong?

      Until the External Offerings Terms are accepted, to_pandas is unavailable, along with third-party Anaconda packages.

      Covered in Python stored procedures: the orchestrating step

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “You specify a SELECT query and a target lag, and Snowflake tracks dependencies and refreshes the data on schedule.”
      ↩︎ Automating data transformation with dynamic tables
      “INCREMENTAL processes only the rows that changed since the last refresh.”
      ↩︎ Automating data transformation with dynamic tables
      “You can also set TARGET_LAG = DOWNSTREAM on intermediate tables so they refresh only when their downstream dependents need fresh data.”
      ↩︎ Automating data transformation with dynamic tables
      “As a general rule, if your logic is expressible as a SQL SELECT statement, it is a candidate for a dynamic table.”
      ↩︎ Key concept
      “Need stored procedures or external functions in the definition.”
      ↩︎ Exam trap 1
      “Require data fresher than 60 seconds (the minimum target lag) or strictly guaranteed refresh timing.”
      ↩︎ Prediction
      “Snowflake infers the dependency graph automatically from the queries you write. You don’t need to declare dependencies or script the execution order.”
      ↩︎ Checkpoint
    2. 2.
      “Because a Python UDF must be a scalar function, it must return one value each time that it is invoked.”
      ↩︎ Python UDFs: one value per row
      “receive batches of input rows as Pandas DataFrames and return batches of results as Pandas arrays or Series”
      ↩︎ Python UDFs: one value per row
    3. 3.
      “The trained model can be uploaded into a Snowflake stage, and can be used to create UDFs to perform inference.”
      ↩︎ Python UDFs: one value per row
    4. 4.
      “Is invoked once for each partition, and before the process method is invoked.”
      ↩︎ Python UDTFs: rows out, state per partition
      “In partition-unaware processing, the handler executes statelessly, ignoring partition boundaries.”
      ↩︎ Python UDTFs: rows out, state per partition
      “Execute long-running initialization that needs to be done only once per partition rather than once per row.”
      ↩︎ Checkpoint
    5. 5.
      “To schedule the execution of these stored procedures, you use tasks.”
      ↩︎ Python stored procedures: the orchestrating step
      “You must use the ORGADMIN role to accept the terms.”
      ↩︎ Python stored procedures: the orchestrating step
      “You can’t use the to_pandas method when interacting with a DataFrame object.”
      ↩︎ Exam trap 2
      “With stored procedures, you can build and run your data pipeline within Snowflake, using a Snowflake warehouse as the compute framework.”
      ↩︎ Checkpoint

    Continue to page 2 of 2

    Training Models in Snowflake: Stored Procedures, Tuning, Sampling, UDTFs and External Functions

    Spotted a mistake, or was something unclear? Tell us.