Read every Teach first section before you copy SQL.
Domain 3 starts here: stages hold files, file formats describe shape,
COPY INTO moves data in or out. We avoid local PUT so Windows Snowsight still works.
Business problem
Riley’s vendor drops CSVs. Morgan wants a repeatable dock: named stage, format object,
load into a table, and the reverse (unload) for a partner. “No mystery desktop folders.”
Solution you will buildPIPELINE.STG_ALPINE_LOAD, CSV/JSON file formats, seed with INSERT,
unload with COPY INTO @stage FROM (SELECT …), validate, reload with transform + ON_ERROR.
Directory tables for file metadata awareness.
Before
Mystery folders
Files on laptops only
No shared format definition
“It worked on my machine”
After
Named dock + COPY
Internal stage in PIPELINE
FF_CSV_CUSTOMERS / FF_JSON
Load · unload · validate
Episode 10 · Start here
Riley draws a loading dock. Dev names every object.
RileyPeer engineer
I keep pasting CSV into worksheets. When the file gets big, that falls over. Where do files live before they’re a table?
DevCoach
On a stage — the loading dock. File format = the packing label (CSV vs JSON). COPY INTO = the forklift in or out.
Your private dock. Fine for scratch; awkward to share with Riley’s role.
LIST @~;
Tied to table
Table stage @%T
Dock attached to one table. Handy for quick loads into that table only.
LIST @%CUSTOMERS_LOAD;
Shared (this lab)
Named internal stage
First-class object you GRANT like a table. Best for team pipelines.
@STG_ALPINE_LOAD
Cloud bucket
External stage
Points at S3 / Azure / GCS via storage integration. Same COPY syntax, different dock location.
s3://alpine-raw/… + storage integration
See it — COPY directions
Load
Stage → table
COPY INTO my_table FROM @stage. Optional SELECT transform, ON_ERROR, VALIDATION_MODE.
files on dock → rows in CUSTOMERS_LOAD_2
Unload
Table → stage
COPY INTO @stage FROM (SELECT …). Writes files out for partners or reload drills.
CUSTOMERS_LOAD → @STG…/customers_unload/
Objects in this lab
1 · SeedINSERT rowsCUSTOMERS_LOAD
2 · UnloadCOPY INTO @stageSTG_ALPINE_LOAD
3 · ValidateVALIDATION_MODEno load yet
4 · ReloadCOPY + transformCUSTOMERS_LOAD_2
Before Step 1
Kai: “Is a stage a table?”
KaiAnalyst
If I LIST a stage and see files, can I SELECT * FROM the stage like a table?
DevCoach
Not as normal rows. You LIST files, query with $1, $2 in a COPY SELECT, or use a directory table for file metadata. Tables hold typed columns after COPY.
1 File formats + named stage
What this does: Creates reusable packing labels (CSV + JSON) and the shared internal dock in PIPELINE, with directory metadata enabled.
SQL · formats + stage
USE ROLE ROLE_CORE_LAB_ADMIN;
USE WAREHOUSE WH_CORE_XS;
USE DATABASE ALPINE_RETAIL;
USE SCHEMA PIPELINE;
CREATE OR REPLACE FILE FORMAT FF_CSV_CUSTOMERS
TYPE = CSV FIELD_DELIMITER = ',' SKIP_HEADER = 1
FIELD_OPTIONALLY_ENCLOSED_BY = '"';
CREATE OR REPLACE STAGE STG_ALPINE_LOAD
FILE_FORMAT = FF_CSV_CUSTOMERS
DIRECTORY = (ENABLE = TRUE);
✓ SHOW STAGES LIKE 'STG_ALPINE%' returns one row
2 Seed the table (INSERT — no PUT)
What this does: Puts three demo customers in CUSTOMERS_LOAD. Snowsight on Windows often can’t PUT local files — INSERT keeps the lab moving.
SQL · create + insert
CREATE OR REPLACE TABLE CUSTOMERS_LOAD (
CUSTOMER_ID NUMBER, FULL_NAME STRING,
EMAIL STRING, COUNTRY STRING
);
INSERT INTO CUSTOMERS_LOAD VALUES
(10, 'Fran Kim', 'fran.kim@example.com', 'US'),
(11, 'Gita Singh', 'gita.singh@example.com', 'CA'),
(12, 'Hugo Diaz', 'hugo.diaz@example.com', 'US');
SELECT * FROM CUSTOMERS_LOAD ORDER BY CUSTOMER_ID;
Want real PUT?
SnowSQL: PUT file://customers.csv @STG_ALPINE_LOAD/raw/ then COPY — commented in sql/10_copy_stages.sql.
✓ Three rows in CUSTOMERS_LOAD
Before Step 3
Morgan: “Can we send a file out?”
MorganManager
Partner asks for a CSV extract. Do we email a spreadsheet from someone’s laptop again?
RileyPeer engineer
COPY INTO @stage FROM (SELECT …) writes files on the dock. Same forklift, opposite direction.
3 Unload to stage + directory list
What this does: Writes CSV files under @STG_ALPINE_LOAD/customers_unload/, lists them, refreshes directory metadata.
SQL · unload
COPY INTO @STG_ALPINE_LOAD/customers_unload/
FROM (
SELECT CUSTOMER_ID, FULL_NAME, EMAIL, COUNTRY
FROM CUSTOMERS_LOAD
)
FILE_FORMAT = (FORMAT_NAME = 'FF_CSV_CUSTOMERS')
OVERWRITE = TRUE HEADER = TRUE;
LIST @STG_ALPINE_LOAD/customers_unload/;
ALTER STAGE STG_ALPINE_LOAD REFRESH;
✓ LIST shows file(s) under customers_unload/
4 VALIDATION_MODE — check without loading
What this does: Parses stage files and reports errors without inserting rows. Safe dry-run before a big load.
SQL · validate
COPY INTO CUSTOMERS_LOAD
FROM @STG_ALPINE_LOAD/customers_unload/
FILE_FORMAT = (FORMAT_NAME = 'FF_CSV_CUSTOMERS')
VALIDATION_MODE = 'RETURN_ERRORS';
What this does: Loads into a second table, uppercasing names and stamping LOADED_AT during COPY. ON_ERROR = CONTINUE keeps good rows if a bad one appears.
SQL · transform load
CREATE OR REPLACE TABLE CUSTOMERS_LOAD_2 (
CUSTOMER_ID NUMBER, FULL_NAME STRING,
EMAIL STRING, COUNTRY STRING,
LOADED_AT TIMESTAMP_NTZ
);
COPY INTO CUSTOMERS_LOAD_2
(CUSTOMER_ID, FULL_NAME, EMAIL, COUNTRY, LOADED_AT)
FROM (
SELECT $1::NUMBER, UPPER($2::STRING), $3::STRING,
$4::STRING, CURRENT_TIMESTAMP()
FROM @STG_ALPINE_LOAD/customers_unload/
)
FILE_FORMAT = (FORMAT_NAME = 'FF_CSV_CUSTOMERS')
ON_ERROR = 'CONTINUE';
✓ Names UPPER · LOADED_AT filled
6 JSON awareness (tiny)
What this does: Reminds you JSON uses a different file format and often lands in VARIANT. Tiny INSERT stands in for a JSON COPY.
SQL · JSON / VARIANT
CREATE OR REPLACE FILE FORMAT FF_JSON_EVENTS
TYPE = JSON STRIP_OUTER_ARRAY = TRUE;
CREATE OR REPLACE TABLE EVENTS_STAGE_LOAD (PAYLOAD VARIANT);
INSERT INTO EVENTS_STAGE_LOAD
SELECT PARSE_JSON(COLUMN1) FROM VALUES
('{"event":"click","user":1}'),
('{"event":"purchase","user":2}');
SELECT * FROM EVENTS_STAGE_LOAD;
✓ Two VARIANT payloads
Exam presses further — say these out loud
Basics moved files through the dock. Cover each answer before you open it.
Q1. User vs named vs external stage — when each?
Say: User (@~) = personal scratch. Named internal = shared team dock with grants. External = files already in cloud storage via storage integration. Table stage (@%) = dock tied to one table.
Q2. How do you TRANSFORM during COPY?
Say:COPY INTO table FROM (SELECT … FROM @stage) — cast columns, UPPER, add defaults like CURRENT_TIMESTAMP(). Transform in the SELECT, not after a blind load, when possible.
Q3. What does PURGE = TRUE do?
Say: After a successful load, delete the loaded files from the stage. Saves clutter; don’t purge if you still need the files for audit/replay.
Q4. File format options you must name on CSV questions?
Say: TYPE, FIELD_DELIMITER, SKIP_HEADER, FIELD_OPTIONALLY_ENCLOSED_BY, NULL_IF / ERROR_ON_COLUMN_COUNT_MISMATCH as needed. JSON: STRIP_OUTER_ARRAY often matters.
Q5. VALIDATION_MODE vs ON_ERROR CONTINUE?
Say: VALIDATION_MODE dry-runs and does not load. ON_ERROR CONTINUE loads good rows and skips bad ones in a real COPY. Different tools — validate first on scary files.
Priya’s wrap-up (say this out loud)
“Domain 3 loading: I created FF_CSV_CUSTOMERS and named stage STG_ALPINE_LOAD in PIPELINE, seeded CUSTOMERS_LOAD with INSERT,
unloaded with COPY INTO @stage FROM SELECT, validated with VALIDATION_MODE, then reloaded with a SELECT transform and ON_ERROR CONTINUE.
Outcome: a repeatable dock without depending on Windows PUT — ready for Snowpipe / streams in later labs.”
Next
Lab 11 — continuous loading (Snowpipe) and related automation. Review Domain 2–3 tips.