Data Transformation
This domain (25% of DEA-C02, the single largest weighted domain alongside Data Movement) tests your ability to reshape data after it lands in Snowflake. Expect scenario questions that hand you a requirement — idempotent upserts, SCD Type 2 history, JSON explosion, reusable business logic, a near-real-time aggregate layer — and ask you to pick the correct construct (MERGE vs. stream+task vs. dynamic table; UDF vs. UDTF vs. stored procedure; view vs. materialized view) and to write or debug the SQL/Python. The exam is heavy on exact semantics: what FLATTEN(OUTER => TRUE) returns, what a materialized view is not allowed to contain, what happens to a transaction when a DDL statement runs inside it, and which dbt materialization produces which DDL. Memorize behaviors and limits, not marketing.
Data transformation patterns: ELT in Snowflake SQL, window functions, MERGE
ELT means land raw data first (COPY INTO → raw/landing table, often with a VARIANT column), then transform with SQL using Snowflake compute. Standard layering: raw → staging (typed, deduped) → intermediate → marts. Use transient tables for intermediate layers (no Fail-safe, cheaper storage) and CTAS (CREATE OR REPLACE TABLE ... AS SELECT) for full rebuilds.
MERGE is the core upsert. Syntax essentials:
MERGE INTO tgt t
USING (SELECT * FROM src QUALIFY ROW_NUMBER() OVER (PARTITION BY id ORDER BY ts DESC)=1) s
ON t.id = s.id
WHEN MATCHED AND s.op = 'D' THEN DELETE
WHEN MATCHED AND s.ts > t.ts THEN UPDATE SET t.val = s.val, t.ts = s.ts
WHEN NOT MATCHED THEN INSERT (id,val,ts) VALUES (s.id,s.val,s.ts);
- Multiple
WHEN MATCHEDclauses are allowed and are evaluated in order; the first matching clause wins. - If more than one source row joins the same target row, the MERGE is nondeterministic. The session parameter
ERROR_ON_NONDETERMINISTIC_MERGEdefaults toTRUE, so the statement errors. Setting it toFALSEmakes Snowflake silently pick one row. The correct fix on the exam is almost always deduplicate the source withQUALIFY ROW_NUMBER() ... = 1, not flipping the parameter. - The sibling parameter for
UPDATE ... FROMisERROR_ON_NONDETERMINISTIC_UPDATE(defaultFALSE).
Window functions are the second pillar. function() OVER (PARTITION BY ... ORDER BY ... frame).
| Concept | Rule to memorize |
|---|---|
Default frame with ORDER BY | RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW |
Default frame without ORDER BY | Entire partition |
Rank functions (ROW_NUMBER, RANK, DENSE_RANK, NTILE, LAG, LEAD) | Cannot take an explicit window frame |
LAST_VALUE returning "wrong" value | Default frame stops at current row — use ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING |
QUALIFY | Snowflake-specific; filters on window-function results after WHERE/GROUP BY/HAVING, before ORDER BY/LIMIT |
IGNORE NULLS | Supported on LAG, LEAD, FIRST_VALUE, LAST_VALUE, NTH_VALUE — the standard "last known value" gap-fill |
Incremental ELT uses streams (CDC) + tasks (scheduling). Stream metadata columns: METADATA$ACTION (INSERT/DELETE), METADATA$ISUPDATE (TRUE for the delete/insert pair produced by an update), METADATA$ROW_ID. A standard stream shows net row-level deltas; an append-only stream shows inserts only (cheaper, no delete/update rows); an insert-only stream is the external-table equivalent. A stream is consumed only inside a DML transaction that reads it; reading in SELECT does not advance the offset.
Bold caption: choosing the right incremental transformation pattern.
Common traps
- Choosing
ERROR_ON_NONDETERMINISTIC_MERGE = FALSEas the "fix" for duplicate source keys — it hides data loss; dedupe instead. - Assuming
WHEREcan filterROW_NUMBER()output. It cannot; you needQUALIFYor a subquery. - Forgetting that consuming a stream requires DML; a task that only
SELECTs from a stream never advances the offset and reprocesses forever. - Believing
MERGEis atomic across multiple statements — a single MERGE is atomic, but a MERGE + a follow-up INSERT needs an explicit transaction. - Confusing
RANK(gaps) withDENSE_RANK(no gaps) andROW_NUMBER(always unique).
Worked scenario. A landing table receives late-arriving CDC with multiple versions of the same order_id in one batch, including hard deletes flagged op='D'. Requirement: target must reflect the latest state, idempotently. Correct approach: stream or batch → dedupe to one row per order_id by highest commit_ts with QUALIFY → single MERGE with WHEN MATCHED AND op='D' THEN DELETE, then WHEN MATCHED THEN UPDATE, then WHEN NOT MATCHED AND op<>'D' THEN INSERT. One statement = one transaction = idempotent and atomic.
Unlock with a paid plan
10 more sections (about 3,258 words) continue below. Every paid plan unlocks all DEA-C02 study guides, the full question bank and mock exams, from $4.97/month.