8.5 Schema Detection, Schema Evolution & Source-Change Architecture
Key Takeaways
- INFER_SCHEMA reads staged Parquet, Avro, ORC, JSON, or CSV files and returns column names, types, and nullability, and CREATE TABLE ... USING TEMPLATE turns that output into a table definition.
- Schema evolution adds new columns and drops NOT NULL from columns missing in new files when the table has ENABLE_SCHEMA_EVOLUTION = TRUE, the COPY uses MATCH_BY_COLUMN_NAME, and the loading role has EVOLVE SCHEMA or OWNERSHIP on the table.
- Schema evolution applies to COPY INTO <table> and Snowpipe (not INSERT); by default one COPY can add at most 100 columns or evolve one schema, and CSV evolution also needs PARSE_HEADER and ERROR_ON_COLUMN_COUNT_MISMATCH = FALSE.
- Snowpipe Streaming's high-performance architecture and the Kafka connector can also evolve table schemas, while VARIANT landing columns absorb source changes without any DDL.
- Architects choose between strict schemas (fail fast on drift), automatic evolution (accept additive changes), and VARIANT landing plus typed downstream models (absorb any change, type later).
Why Source Changes Are an Architecture Problem
The ARA-C01 blueprint (objective 3.1) asks architects to design loading solutions that handle architecture changes: schema detection and table schema evolution and data source changes. In practice this means deciding what should happen when tomorrow's files contain a column that today's table does not have.
| Strategy | Behavior on a new source column | Best for |
|---|---|---|
| Strict schema | Load fails or the column is ignored; engineers change DDL deliberately | Regulated or contract-driven feeds |
| Automatic schema evolution | Snowflake adds the column during the load | Additive, trusted feeds from many producers |
| VARIANT landing | Whole record lands in a VARIANT column; nothing breaks | Highly variable JSON/event data; typing happens downstream |
Schema Detection with INFER_SCHEMA
INFER_SCHEMA is a table function that reads staged files and returns the detected column definitions: COLUMN_NAME, TYPE, NULLABLE, EXPRESSION, FILENAMES, and ORDER_ID. It supports Apache Parquet, Apache Avro, ORC, JSON, and CSV files.
CREATE FILE FORMAT raw.formats.parquet_ff TYPE = PARQUET;
-- Inspect what Snowflake detects in the staged files
SELECT *
FROM TABLE(INFER_SCHEMA(
LOCATION => '@raw.stages.orders_stage/2026/09/',
FILE_FORMAT => 'raw.formats.parquet_ff',
MAX_FILE_COUNT => 10));
-- Create a table whose columns come straight from the detected schema
CREATE TABLE raw.orders
USING TEMPLATE (
SELECT ARRAY_AGG(OBJECT_CONSTRUCT(*))
FROM TABLE(INFER_SCHEMA(
LOCATION => '@raw.stages.orders_stage/2026/09/',
FILE_FORMAT => 'raw.formats.parquet_ff')));
Useful details:
USING TEMPLATEalso works withCREATE EXTERNAL TABLEandCREATE ICEBERG TABLE.- For CSV, the file format needs
PARSE_HEADER = TRUEso column names come from the header row. GENERATE_COLUMN_DESCRIPTIONturnsINFER_SCHEMAoutput into a column list you can paste into DDL.MAX_FILE_COUNTandMAX_RECORDS_PER_FILElimit how much data is scanned, which matters for very large prefixes.
Table Schema Evolution
Schema evolution lets a load change the target table automatically:
- Adds new columns found in the incoming files.
- Drops the
NOT NULLconstraint from columns that are missing in new data files.
All of these must be true for a file load to evolve the table:
- The table has
ENABLE_SCHEMA_EVOLUTION = TRUE(set atCREATE TABLEor withALTER TABLE). - The
COPY INTO <table>usesMATCH_BY_COLUMN_NAME(CASE_SENSITIVEorCASE_INSENSITIVE). - The role running the load has
EVOLVE SCHEMAorOWNERSHIPon the table. - For CSV, the file format uses
PARSE_HEADER = TRUEandERROR_ON_COLUMN_COUNT_MISMATCH = FALSE.
ALTER TABLE raw.orders SET ENABLE_SCHEMA_EVOLUTION = TRUE;
GRANT EVOLVE SCHEMA ON TABLE raw.orders TO ROLE loader_role;
COPY INTO raw.orders
FROM @raw.stages.orders_stage
FILE_FORMAT = (FORMAT_NAME = 'raw.formats.parquet_ff')
MATCH_BY_COLUMN_NAME = CASE_INSENSITIVE;
Limits and scope:
- Evolution applies to
COPY INTO <table>and Snowpipe loads;INSERTstatements never evolve a schema. - By default a single
COPYcan add at most 100 columns or evolve one schema; Snowflake Support can raise this. - Snowpipe Streaming (high-performance architecture) supports schema evolution for standard tables and Snowflake-managed Iceberg tables (adding top-level columns), and the Kafka connector with Snowpipe Streaming Classic supports schema detection and evolution.
- Evolution is additive: it does not rename columns, change types, or drop columns. Renames and type changes still require planned DDL.
Designing for Data Source Changes
A robust layered design absorbs change at the edge and exposes stable contracts to consumers:
- Landing layer: either evolve automatically (typed columns) or land raw records in a
VARIANTcolumn plus metadata (METADATA$FILENAME, load timestamp). VARIANT landing never fails on new attributes. - Transformation layer: dynamic tables, streams and tasks, or dbt models project the fields you actually support, with explicit casts. New source fields do not reach consumers until someone models them.
- Consumption layer: secure views or curated tables keep column names and types stable, so BI tools and shares do not break.
- Monitoring: compare
INFER_SCHEMAoutput with the table's columns in a scheduled task or alert, and reviewLOAD_HISTORY/COPY_HISTORYerrors after source releases.
Exam Trap: Enabling
ENABLE_SCHEMA_EVOLUTIONalone does nothing if theCOPYstatement maps columns by position. WithoutMATCH_BY_COLUMN_NAME, the load cannot evolve the table.
Worked Example: Evolving a CSV Partner Feed
A partner sends daily CSV files with a header row. In March they add a loyalty_tier column, and in June a middle_name column is dropped from their export.
CREATE FILE FORMAT raw.formats.partner_csv
TYPE = CSV
PARSE_HEADER = TRUE -- read column names from the header row
ERROR_ON_COLUMN_COUNT_MISMATCH = FALSE; -- required for CSV schema evolution
CREATE TABLE raw.partner_customers
USING TEMPLATE (
SELECT ARRAY_AGG(OBJECT_CONSTRUCT(*))
FROM TABLE(INFER_SCHEMA(LOCATION => '@raw.stages.partner/2026/01/',
FILE_FORMAT => 'raw.formats.partner_csv')))
ENABLE_SCHEMA_EVOLUTION = TRUE;
COPY INTO raw.partner_customers
FROM @raw.stages.partner
FILE_FORMAT = (FORMAT_NAME = 'raw.formats.partner_csv')
MATCH_BY_COLUMN_NAME = CASE_INSENSITIVE;
What happens:
- March: the load adds
LOYALTY_TIERto the table automatically; earlier rows hold NULL in it. - June: files no longer contain
MIDDLE_NAME. If that column wasNOT NULL, schema evolution drops the NOT NULL constraint so loads keep working; the column stays in the table with NULLs for new rows. - The load history (
COPY_HISTORY,LOAD_HISTORY) and the table's DDL show the change, so downstream owners can decide whether to model the new field.
Changes Schema Evolution Does Not Handle
| Source change | What happens | Architect's response |
|---|---|---|
| New column | Added automatically (if evolution is enabled) | Decide whether downstream layers expose it |
| Column missing in new files | NOT NULL dropped; column kept | Confirm with the source whether it is deprecated |
| Column renamed | Treated as a new column; the old one stops receiving data | Map old and new names in the transformation layer |
| Type changed (for example number to text) | Not evolved; values that no longer convert cause load errors | Land raw values (VARIANT or VARCHAR) or coordinate a planned DDL change |
| New file format or delimiter | Not detected by evolution | Version the file format object and route files by prefix |
For external tables and Iceberg tables, USING TEMPLATE can create the initial definition from INFER_SCHEMA, but later changes are handled by refreshing or altering those tables (and, for Iceberg, by the table format's own schema evolution) rather than by ENABLE_SCHEMA_EVOLUTION.
A table has ENABLE_SCHEMA_EVOLUTION = TRUE. The nightly load runs COPY INTO raw.events FROM @events_stage FILE_FORMAT = (TYPE = PARQUET); and a new column that appeared in the files is not added to the table. What is missing?
An architect wants to create a table whose columns exactly match a set of staged Avro files, without hand-writing the DDL. Which approach does Snowflake provide?
Source teams frequently add optional attributes to JSON events, and downstream BI dashboards must never break when that happens. Which design best meets both needs?