Reference hub · Tool-belt series · Snowflake
You already write working queries. This roadmap takes the next steps: Snowflake-native SQL that makes you fast, the engine and observability layer that make you credible, and — the flagship — a repeatable playbook for the question your stakeholders keep asking: "why is Snowflake missing data that's in SAP?" Cortex Code ("CoCo") runs through the whole thing as your always-on resource.
Same rules as the DAX Studio × Tabular Editor roadmap. Work each part top to bottom — Parts A and B run in parallel (write better SQL while learning what the engine does with it). Part C is the flagship: it turns "Snowflake is missing SAP data" from a fire drill into a 30-minute triage you can run on demand. Part D makes Cortex Code your default first stop for all of it, then closes with a capstone investigation.
Check items off as you go — sections, parts, and the top bar all track progress. Progress saves automatically in this browser on this device; it resets if you clear browser data or open the file elsewhere.
When a request comes in that this page doesn't cover, bring it back to Claude and ask for a new section — it's built to be extended like the rest of your tool belt.
Half of all "missing data" tickets are actually context problems: wrong role, wrong database, a filtered secure view. Before debugging anyone else's data, get ruthless about knowing your own execution context at all times.
Learn this
Go deeper
These are the constructs that separate "I can write queries" from "I can answer any question in one pass." QUALIFY alone will change how you write dedup and latest-record logic — which is exactly the logic CDC pipelines from SAP depend on.
Learn this
-- The CDC backbone: latest record per business key
SELECT *
FROM raw.sap_bkpf
QUALIFY ROW_NUMBER() OVER (
PARTITION BY bukrs, belnr, gjahr -- company, doc no, fiscal year
ORDER BY _loaded_at DESC
) = 1;
Go deeper
Many ingestion tools land source payloads as JSON in a VARIANT column before anything is flattened into typed columns. If your SAP pipeline does this, the raw truth of what arrived lives in VARIANT — and you need to be able to read it.
Learn this
Go deeper
Time Travel lets you query a table as it existed minutes, hours, or days ago. For "the data was there yesterday and now it's gone" tickets, this is not a nice-to-have — it's the difference between guessing and knowing exactly when rows disappeared.
Learn this
-- "It was there yesterday": what disappeared in the last 24h?
SELECT belnr, gjahr, bukrs
FROM curated.fact_gl_docs AT(OFFSET => -86400) -- 24h ago
MINUS
SELECT belnr, gjahr, bukrs
FROM curated.fact_gl_docs; -- now
Go deeper
You don't own the SAP ingestion pipeline, but you'll debug its output. That means reading its parts on sight: stages where files land, COPY INTO that loads them, streams that track changes, tasks and dynamic tables that transform on a schedule. Each one is a place rows can silently stop.
Learn this
Go deeper
ON_ERROR = 'CONTINUE' is the single most common silent-row-loss setting in the wild: the load "succeeds" while quietly skipping bad rows. When you meet a COPY statement, check that option first.Snowflake separates storage (immutable micro-partitions in cloud storage) from compute (virtual warehouses you spin up and down). Once this model clicks, warehouse sizing, pruning, caching, and cost all become predictable instead of mysterious.
Learn this
Go deeper
Every query gets a visual execution profile. Learn to read four numbers — partitions scanned vs total, bytes spilled, join output rows, queue time — and you can diagnose 90% of slow queries in under a minute.
Learn this
Go deeper
This is the section that makes Part C possible. Snowflake records every query, every load, every task run, and every DML operation into queryable views. "Did the load run? Did it fail? Who deleted those rows?" are all one SELECT away — if you know which view to ask.
Learn this
-- Did the SAP load actually run today, and did it drop rows?
SELECT file_name, last_load_time, row_count, row_parsed,
error_count, first_error_message, status
FROM TABLE(information_schema.copy_history(
table_name => 'RAW.SAP_BKPF',
start_time => DATEADD('hour', -24, CURRENT_TIMESTAMP())))
ORDER BY last_load_time DESC;
Go deeper
$SYSTEM.TMSCHEMA_MEASURES to make a model document itself. The account documents itself; most people just never ask. Being the person who asks is the whole job.You won't administer the account, but understanding cost and governance makes you a better citizen and a sharper debugger — and "this recon query costs pennies to run daily" is a sentence stakeholders like hearing.
Learn this
Go deeper
SAP data reaches Snowflake through one of a few extraction mechanisms, then usually hops through raw → staging → curated layers inside Snowflake. Every hop is a place rows can stop. Your first mission is a one-page diagram of your company's actual pipeline — the same "map before opinions" move as your Power BI discovery phase.
Learn this
Go deeper
Almost every "Snowflake is missing SAP data" ticket resolves to one of these ten causes. Internalize the catalog and you stop searching randomly — you check the likely suspects in order of probability and cost-to-check.
Cause 01 · Latency
User compares live SAP against a Snowflake copy refreshed hours ago, or a Power BI import refreshed yesterday. Always establish "as of when?" first. Cheapest check, most common answer.
Cause 02 · Failed / partial load
COPY errors, task failures, Snowpipe backlog, warehouse timeout mid-load. COPY_HISTORY and TASK_HISTORY (B8) answer this in one query. Watch for ON_ERROR='CONTINUE' quietly skipping rows.
Cause 03 · Delta / CDC gap
Trigger dropped during SAP maintenance, delta queue reset, CDC high-water mark skipped a window, initialization ran while transactions were posting. Symptom: a specific date range is thin; rows before and after are fine.
Cause 04 · Deletions & reversals
Or the inverse of missing: hard deletes and document reversals in SAP that the pipeline can't see (many extractors don't capture deletes), leaving Snowflake with phantom rows — or dropping reversals so totals disagree.
Cause 05 · Extraction filters
SLT/connector filters on company code, year, doc type, or client (MANDT!) that exclude the rows in question. Check the extraction config, not the data. Frequently the answer for "new company code has no data."
Cause 06 · Transformation drop
Inner joins that should be left joins, WHERE clauses, dedup logic keeping the wrong record, failed lookups nulling keys. The raw-vs-curated anti-join (C12) catches this precisely.
Cause 07 · Semantic mismatch
Posting date vs entry date, document currency vs local currency, SAP fiscal periods vs calendar months, timezone offsets, special periods (13–16). The counts differ because the definitions differ.
Cause 08 · Key mangling
Leading zeros stripped from ALPHA-converted fields (BELNR!), trims, case changes, composite keys missing MANDT or BUKRS. The row "missing" from a join is present — its key just doesn't match anymore.
Cause 09 · Archived / logic-hidden
Archived documents gone from base tables but expected in reports; pool/cluster data extracted without application logic; CDS views applying authorization or status filters the raw table doesn't.
Cause 10 · Access, not absence
Row access policies, secure views, masking, or role grants filtering what this user's query returns. Compare counts as an elevated role before declaring anything missing.
Learn this
Random searching never terminates; a drill-down always does. Start at total counts, narrow to the period where they diverge, anti-join to the exact keys, then compare columns on those keys. Each step shrinks the haystack by orders of magnitude. Get an SAP-side extract (from the basis/pipeline team, or a CDS view export) as your comparison baseline.
Learn this
-- Step 2: where do the counts diverge? (SAP extract landed as a table/stage)
SELECT COALESCE(s.period, f.period) AS period,
s.cnt AS sap_rows, f.cnt AS snowflake_rows,
s.cnt - f.cnt AS gap
FROM (SELECT monat AS period, COUNT(*) cnt FROM recon.sap_bkpf_extract
WHERE gjahr = 2026 GROUP BY 1) s
FULL OUTER JOIN
(SELECT fiscal_period, COUNT(*) cnt FROM curated.fact_gl_docs
WHERE fiscal_year = 2026 GROUP BY 1) f
ON s.period = f.period
ORDER BY 1;
-- Step 3: exactly which documents are missing?
SELECT s.bukrs, s.belnr, s.gjahr
FROM recon.sap_bkpf_extract s
LEFT JOIN curated.fact_gl_docs f
ON f.company_code = s.bukrs
AND f.doc_number = LTRIM(s.belnr, '0') -- cause 08 hiding in plain sight?
AND f.fiscal_year = s.gjahr
WHERE f.doc_number IS NULL
AND s.monat = 4; -- the divergent period from step 2
Go deeper
Once the playbook works manually, productize it — the same instinct as your enforcement gate: deterministic checks, run automatically, humans only see exceptions. This is also a flagship agentic-AI deliverable: CoCo can draft every object in this section.
Learn this
Go deeper
CoCo's edge over any external AI is context: it can see your object names, metadata, and account state, and it respects RBAC — it only works with what your role can see. Treat it like you treat Copilot agent mode in VS Code: precise charters, explicit context, you review everything before it runs.
Learn this
-- A charter-style CoCo prompt for a missing-data ticket:
-- "Treasury reports document 4700012345 (company 1000, FY2026) exists in
-- SAP BKPF but not in CURATED.FACT_GL_DOCS. Write three queries:
-- 1) search RAW.SAP_BKPF for that key including leading-zero variants,
-- 2) check copy_history for RAW.SAP_BKPF over the last 7 days,
-- 3) show the DDL of every view between RAW and CURATED for this table.
-- Do not modify anything."
Go deeper
Take the next real "Snowflake is missing SAP data" request — or resurrect a past one — and run it end to end with this page's toolkit. Like the model tune-up capstone, this is the exercise that makes everything permanent: you finish with a resolved ticket, a reusable recon asset, and a write-up.
The mission
Success looks like