Study guide
10.1 Semi-Structured Data: VARIANT, OBJECT & ARRAY Processing
SnowPro Advanced: Architect ARA-C01
Track your progress as you read
Finished sections get a check mark.
1 Introduction: The ARA-C01 Exam
About the SnowPro Advanced: Architect (ARA-C01) Exam
2 Chapter 1: Account Strategy & Organization Architecture
1.1 Multi-Account Strategy & Snowflake Organizations 1.2 Snowflake Parameter Hierarchy & Configuration 1.3 Database and Schema Design Architecture
3 Chapter 2: Identity, Access Control & Storage Integrations
2.1 Role-Based Access Control Architecture & Hierarchy 2.2 Secondary Roles, Object Ownership & Delegated Administration 2.3 Cloud Storage Integrations & IAM Trust Policies 2.4 Identity Federation, SSO, SCIM & Authentication Policies
4 Chapter 3: Governance, Data Privacy, Network Security & Compliance
3.1 Column-Level Masking & Row Access Policies 3.2 Object Tagging, Tag-Based Masking & Audit Governance 3.3 Encryption, Key Management & Tri-Secret Secure 3.4 Aggregation Policies, Projection Policies & External Tokenization 3.5 Network Policies, Network Rules, External Access & Private Connectivity 3.6 Snowflake Editions & Compliance Architecture (PCI DSS, PII & PHI)
5 Chapter 4: Data Modeling in Snowflake
4.1 Dimensional Modeling: Star vs. Snowflake Schemas 4.2 Data Vault 2.0 Architecture in Snowflake 4.3 Relational Constraints & Query Optimizer Integration
6 Chapter 5: Data Sharing & Collaboration Solutions
5.1 Direct Data Sharing Architecture & Security 5.2 Managed Reader Accounts for External Consumers 5.3 Snowflake Marketplace, Data Exchange & Clean Rooms
7 Chapter 6: Multi-Cloud, Multi-Region & Business Continuity
6.1 Database & Account Replication Mechanics 6.2 Disaster Recovery Architecture & Client Redirect 6.3 Cross-Cloud Auto-Fulfillment & Listing Replication 6.4 Data Protection: Time Travel, Fail-safe & Storage Costs
8 Chapter 7: Environments, Lakehouse, DevOps & AI/ML Applications
7.1 Multi-Tier Environment Strategy & Zero-Copy Cloning 7.2 Data Lakehouse Architecture & Apache Iceberg 7.3 DataOps, CI/CD & Snowflake Git Integration 7.4 AI/ML Pipelines & Applications: Cortex AI, ML Functions, Snowpark Container Services, Streamlit & Native Apps
9 Chapter 8: Data Ingestion Architecture
8.1 Bulk Ingestion Architecture: COPY INTO Optimization 8.2 Snowpipe Continuous Automated Ingestion 8.3 Snowpipe Streaming & Real-Time Ingestion 8.4 Data Unloading Architecture & Cloud Export Patterns 8.5 Schema Detection, Schema Evolution & Source-Change Architecture
10 Chapter 9: Continuous Pipelines & Stream Transformation
9.1 Change Data Capture (CDC) with Snowflake Streams 9.2 Task Orchestration, DAGs & Serverless Scheduling 9.3 Declarative Data Pipelines with Dynamic Tables 9.4 Views, Materialized Views & Transformation Table Design
11 Chapter 10: Semi-Structured Data & Ecosystem Tooling
10.1 Semi-Structured Data: VARIANT, OBJECT & ARRAY Processing 10.2 Snowflake Ecosystem Connectors, Drivers & SQL API 10.3 Snowpark, User-Defined Functions & Stored Procedures
12 Chapter 11: Virtual Warehouse Architecture & Concurrency
11.1 Virtual Warehouse Architecture, Sizing & Credit Economics 11.2 Multi-Cluster Warehouses & Concurrency Scaling Policies 11.3 Warehouse Types & Query Acceleration Service
13 Chapter 12: Query Profiling & Performance Diagnostics
12.1 Query Profile Deep-Dive & Bottleneck Analysis 12.2 Memory Spilling, Warehouse Queuing & Timeout Governance 12.3 Three-Tier Caching Architecture & Result Reuse
14 Chapter 13: Storage Optimization, Cost Governance & Observability
13.1 Micro-Partitioning & Automatic Clustering 13.2 Search Optimization Service (SOS) & Point Lookup Acceleration 13.3 Resource Monitors, Financial Governance & Usage Observability 13.4 Warehouse Monitoring, Alerts, Event Tables & Performance Explorer
10.1 Semi-Structured Data: VARIANT, OBJECT & ARRAY Processing
Key Takeaways Explain This Topic AI
VARIANT, OBJECT, and ARRAY store semi-structured data; a single VARIANT value can hold up to 128 MB of uncompressed data (the same limit applies to OBJECT and ARRAY values).
Colon and dot notation (col:field.subfield), bracket notation (col['field']), or double-quoted element names return VARIANT values; explicit casts (::TYPE) produce typed relational values for predictable comparisons, joins, and output.
Snowflake differentiates between SQL NULL (indicating a missing key or non-existent path) and JSON null (an explicit 'null' literal value), requiring IS_NULL_VALUE() or PARSE_JSON('null') to evaluate correctly.
The FLATTEN lateral table function converts nested arrays and objects into relational rows (outputting seq, key, path, index, value, this), using outer => TRUE to preserve parent rows containing empty arrays.
During write operations, Snowflake's columnar engine automatically detects repeating paths and data types, shredding them into physical columnar sub-columns within micro-partitions to enable min/max metadata pruning.
Last updated: September 2026
10.1 Semi-Structured Data: VARIANT, OBJECT & ARRAY Processing
Modern cloud data platforms must ingest, query, and transform vast quantities of semi-structured data—such as JSON event streams, IoT sensor payloads, Avro event logs, and Parquet data lake files—without requiring slow and rigid upfront schema definitions. Snowflake was architected from the ground up to provide native, first-class support for semi-structured data, combining the flexibility of schema-on-read with the query performance, compression, and micro-partition pruning of a columnar relational database.
For the SnowPro Advanced: Architect (ARA-C01) exam, candidates must master semi-structured data types, traversal syntax, casting subtleties, lateral flattening mechanics, null-value discrimination, and the underlying physical storage architecture that enables sub-column columnarization and micro-partition pruning.
Semi-Structured Data Types: VARIANT, OBJECT & ARRAY
Snowflake provides three specialized native data types specifically designed to store and manipulate semi-structured hierarchical structures: ┌────────────────────────────────────────┐
- │ VARIANT │
- │ Universal Semi-Structured Container │
- │ Holds any primitive or complex type │
- │ (Max 128 MB uncompressed) │
- └──────────────────┬─────────────────────┘
- │
- ┌────────────────────────┴────────────────────────┐
- ▼ ▼
- ┌─────────────────────────────────┐ ┌─────────────────────────────────┐
- │ OBJECT │ │ ARRAY │
- │ Key-Value Dictionary Collection │ │ Ordered, Dense List of Elements │
- │ Keys: String | Values: VARIANT │ │ Index: 0 to N-1 | Values: VARIANT│
- │ Example: {"city": "San Mateo"} │ │ Example: ["US", "CA", "UK"] │
- └─────────────────────────────────┘ └─────────────────────────────────┘
- 1. VARIANT
The VARIANT type is Snowflake's universal semi-structured container. A VARIANT column can store any Snowflake primitive value (e.g., VARCHAR , NUMBER , BOOLEAN , TIMESTAMP_NTZ ) or complex hierarchical structure ( OBJECT , ARRAY ).
Size Limit : An individual VARIANT value can hold up to 128 MB of uncompressed data . Very large documents (for example, one huge JSON array) should be split into rows during loading — STRIP_OUTER_ARRAY = TRUE in the JSON file format turns top-level array elements into separate rows.
Universal Ingestion : Formats including JSON, Avro, ORC, Parquet, and XML can be loaded directly into a VARIANT column without prior parsing.
2. OBJECT
An OBJECT represents an unordered collection of key-value pairs (analogous to a JSON object, Python dictionary, or hash map).
Keys : Always VARCHAR strings.
Values : Always of type VARIANT , allowing nested values to be primitives, arrays, or further nested objects.
Size Limit : An OBJECT value can be up to 128 MB.
3. ARRAY
An ARRAY represents an ordered, zero-indexed sequence of elements (analogous to a JSON array or Python list).
Indexing : Zero-based ( array_col[0] accesses the first element).
Element Types : Each element is stored as a VARIANT , enabling heterogeneous arrays containing mixed data types (e.g., [100, 'Gold', true, {'discount': 0.15}] ).
Size Limit : The combined size of all values in an ARRAY can be up to 128 MB.
Semi-Structured Data Types Comparison
Data Type Structural Model Indexing Mechanism Maximum Size Common Usage
VARIANT Universal polymorphic container Key path or numerical index 128 MB (uncompressed) Raw ingestion landing zones, flexible payload storage, poly-schema staging
OBJECT Key-value dictionary ( {k: v} ) String key lookup ( obj['key'] ) 128 MB Structured key-value lookup tables, config metadata, mapped entity attributes
ARRAY Ordered sequence ( [v0, v1, ...] ) Integer offset index ( arr[0] ) 128 MB (combined values) Order line items, tag collections, timestamp series, repeating sub-records
Traversal Syntax, Path Extraction & Type Casting
Querying nested attributes within semi-structured data relies on two primary traversal conventions: colon notation and bracket notation .
- Colon Traversal vs. Bracket Traversal
- Consider an ingestion table containing raw customer event logs: CREATE OR REPLACE TABLE raw_events (
- event_id VARCHAR(64),
- ingested_at TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP(),
- payload VARIANT
- );
- Sample payload JSON value: {
- "customer": {
- "account-id": "ACC-98421",
- "first name": "Jane",
- "contact": {
- "email": "jane.doe@enterprise.com",
- "phones": ["+1-555-0199", "+1-555-0144"]
- }
- },
- "transaction": {
- "amount": 450.75,
- "currency": "USD",
- "tax_exempt": false
- }
- }
- 1. Colon Notation ( : ) and Dot Syntax
- Used for traversing standard alphanumeric object keys: SELECT
- payload:customer.contact.email AS contact_email,
- payload:transaction.amount AS raw_amount
- FROM raw_events;
- 2. Bracket Notation ( ['<key>'] or [<index>] )
Mandatory when object keys contain special characters, hyphens, spaces, dots, or start with digits, as well as for array index access: SELECT
- - Hyphenated key requires bracket notation
- payload:customer['account-id'] AS account_id,
- - Key containing spaces requires bracket notation
- payload:customer['first name'] AS first_name,
- - Array zero-based element access
- payload:customer.contact.phones[0] AS primary_phone
- FROM raw_events;
Exam Trap : If you write payload:customer.account-id , Snowflake interprets -id as a subtraction operator rather than part of the key, giving an error or an unexpected result. When a key contains a hyphen, space, or other character that is not valid in an identifier, either use bracket notation ( payload:customer['account-id'] ) or enclose the element name in double quotes ( payload:customer."account-id" ). Key names are case-sensitive.
Path Extraction Returns VARIANT: The Double-Quote Dilemma
A critical concept on the ARA-C01 exam is that all path extractions using colon or bracket syntax return a VARIANT data type , not a SQL primitive: -- Query returning uncast VARIANT
- SELECT payload:customer.contact.email FROM raw_events;
- - Output: "jane.doe@enterprise.com" (includes literal enclosing double quotes!)
The double quotes appear because the output is a VARIANT that contains a string, not a VARCHAR. Snowflake can still compare it with a literal through implicit conversion, but relying on implicit conversion is fragile:
Comparisons and sorts between VARIANT values follow VARIANT rules, which can differ from the typed behavior you expect (for example, numbers stored as strings in one file and as numbers in another).
Joins and GROUP BY on uncast paths carry VARIANT values into results, BI tools, and downstream tables.
Explicit casts document the intended type and fail loudly (or return NULL with TRY_ functions) when data does not match. -- Works through implicit conversion, but the intent and type are unclear
SELECT * FROM raw_events WHERE payload:customer.contact.email = 'jane.doe@enterprise.com';
- - Preferred: explicit, typed comparison
- SELECT * FROM raw_events WHERE payload:customer.contact.email::VARCHAR = 'jane.doe@enterprise.com';
- Explicit Casting ( :: and Conversion Functions)
To strip the enclosing JSON quotes and convert semi-structured attributes into first-class relational types, apply explicit casting with the double colon ( :: ) operator or conversion functions: SELECT
- payload:customer['account-id']::VARCHAR(32) AS account_id,
- payload:customer['first name']::VARCHAR(100) AS first_name,
- payload:customer.contact.email::VARCHAR(256) AS email,
- payload:customer.contact.phones[0]::VARCHAR(32) AS primary_phone,
- payload:transaction.amount::NUMBER(12,2) AS txn_amount,
- payload:transaction.tax_exempt::BOOLEAN AS is_tax_exempt
- FROM raw_events
- WHERE payload:customer.contact.email::VARCHAR = 'jane.doe@enterprise.com';
- Safe Type Casting: TRY_CAST and TRY_TO_*
In real-world ingestion feeds, semi-structured fields often contain corrupted or unexpected data (e.g., an alphabetical string in an amount field). A standard cast ( payload:transaction.amount::NUMBER ) aborts the entire query upon encountering an invalid conversion. Use TRY_CAST or TRY_TO_NUMBER to return NULL on conversion errors without failing the query: SELECT
event_id,
-- Aborts on malformed numeric data:
-- payload:transaction.amount::NUMBER(10,2) AS unsafe_amt,
-- Safely returns NULL on malformed data:
- TRY_CAST(payload:transaction.amount::VARCHAR AS NUMBER(10,2)) AS safe_amount,
- TRY_TO_TIMESTAMP_NTZ(payload:event_timestamp::VARCHAR) AS safe_timestamp
- FROM raw_events;
The Null Dilemma: SQL NULL vs. JSON null
One of the most frequently tested semi-structured concepts on the SnowPro Advanced: Architect exam is the distinction between a SQL NULL and an explicit JSON null literal ( IS_NULL_VALUE ).
Understanding the Two Types of Nulls
SQL NULL (Missing Key or Undefined Path) :
Occurs when a queried key or path does not exist in the semi-structured object, or when the entire relational column is NULL .
Evaluates to TRUE for standard SQL IS NULL predicates.
JSON null ( null Literal Value) :
Occurs when a key explicitly exists in the JSON payload with a value of null (e.g., {"secondary_email": null} ).
In Snowflake, an explicit JSON null is a valid, non-null VARIANT value that represents the JSON literal null .
Crucial : A JSON null literal evaluates to FALSE when tested with IS NULL !
- Demonstrating the Null Behavior WITH sample_data AS (
- SELECT PARSE_JSON('{
- "user_id": 101,
- "primary_email": "user@corp.com",
- "secondary_email": null
- - "phone" key is completely missing
- }') AS v
- )
- SELECT
- - Key is completely missing: SQL NULL
- v:phone IS NULL AS phone_is_sql_null, -- Returns TRUE
- IS_NULL_VALUE(v:phone) AS phone_is_json_null, -- Returns FALSE
- - Key exists with explicit null literal: JSON null
- v:secondary_email IS NULL AS email2_is_sql_null, -- Returns FALSE (Exam Trap!)
- IS_NULL_VALUE(v:secondary_email) AS email2_is_json_null, -- Returns TRUE
- v:secondary_email = PARSE_JSON('null') AS email2_equals_json_null, -- Returns TRUE
- - Casting JSON null to VARCHAR strips the variant wrapper and yields a SQL NULL
- v:secondary_email::VARCHAR IS NULL AS cast_email2_is_sql_null -- Returns TRUE
- FROM sample_data;
- Null Handling Decision Matrix
- Expression Scenario Value in Document col:path IS NULL IS_NULL_VALUE(col:path) col:path::VARCHAR IS NULL
- Key does not exist {"a": 1} -> query col:b TRUE FALSE TRUE
- Key has explicit null {"a": 1, "b": null} -> query col:b FALSE (Trap!) TRUE TRUE
- Key has valid string {"a": 1, "b": "text"} -> query col:b FALSE FALSE FALSE
- Entire column is NULL SQL NULL row TRUE NULL TRUE
Architect Exam Takeaway : To reliably filter out records where an attribute is either omitted OR explicitly set to JSON null, cast the attribute to a SQL primitive ( col:path::VARCHAR IS NOT NULL ) or use compound logic: WHERE col:path IS NOT NULL AND NOT IS_NULL_VALUE(col:path) .
Relational Transformation Functions
Snowflake provides a suite of specialized functions for parsing, validating, constructing, and decomposing semi-structured data.
Parsing and Validation Functions
PARSE_JSON(string_expr) :
Converts a valid JSON-formatted string into a VARIANT object.
If the input string contains invalid JSON syntax, PARSE_JSON aborts the query execution with a parsing error.
TRY_PARSE_JSON(string_expr) :
Safely parses a JSON string into a VARIANT . If parsing fails due to malformed JSON, it returns SQL NULL instead of aborting the query.
Architect Recommendation : Always use TRY_PARSE_JSON in ingestion ELT pipelines to isolate corrupted records into an error handling quarantine table.
CHECK_JSON(string_expr) :
Validates a string expression for JSON compliance.
Returns NULL if the string is 100% syntactically valid JSON.
Returns a detailed error message string describing the exact syntax defect if invalid. -- Quarantining invalid payloads during staging
- INSERT INTO dead_letter_queue (raw_string, error_message)
- SELECT
- raw_payload,
- CHECK_JSON(raw_payload) AS error_reason
- FROM landing_stage_raw
- WHERE CHECK_JSON(raw_payload) IS NOT NULL;
- Construction Functions: OBJECT_CONSTRUCT & ARRAY_AGG
OBJECT_CONSTRUCT(k1, v1, k2, v2, ...) :
Constructs an OBJECT from key-value pairs.
Omission Rule : Any key whose corresponding value evaluates to a SQL NULL is completely omitted from the resulting object.
OBJECT_CONSTRUCT_KEEP_NULL(k1, v1, ...) :
Retains keys whose values evaluate to SQL NULL , explicitly storing them as JSON null literals ( "key": null ).
ARRAY_CONSTRUCT(v1, v2, ...) :
Constructs an ARRAY containing the provided arguments.
ARRAY_AGG(expr) :
Aggregate function that rolls multiple relational rows into a single ordered ARRAY grouped by a dimensional key. -- Aggregating relational order lines into a nested JSON customer document
- SELECT
- c.customer_id,
- OBJECT_CONSTRUCT(
- 'customer_id', c.customer_id,
- 'customer_name', c.name,
- 'orders', ARRAY_AGG(OBJECT_CONSTRUCT(
- 'order_id', o.order_id,
- 'total_amount', o.total,
- 'order_date', o.order_date
- )) WITHIN GROUP (ORDER BY o.order_date DESC)
- ) AS customer_profile_json
- FROM customers c
- JOIN orders o ON c.customer_id = o.customer_id
- GROUP BY c.customer_id, c.name;
- Unnesting Data with the FLATTEN Table Function
The FLATTEN function is a lateral table function used to explode an ARRAY or OBJECT into individual relational rows. It is almost always paired with the LATERAL join operator.
- FLATTEN Syntax and Parameters TABLE(FLATTEN(
- input => <variant_array_or_object>,
- path => '<string_path_inside_input>',
- outer => TRUE | FALSE,
- recursive => TRUE | FALSE,
- mode => 'BOTH' | 'ARRAY' | 'OBJECT'
- ))
Parameter Type Default Architectural Behavior
input VARIANT , OBJECT , ARRAY (Required) The semi-structured expression to explode.
path VARCHAR '' (root) Optional path inside the input to target for flattening.
outer BOOLEAN FALSE When FALSE (inner join), parent rows with empty arrays ( [] ) or NULL values are excluded. When TRUE (left outer join), parent rows are preserved with NULL in the exploded columns.
recursive BOOLEAN FALSE When TRUE , recursively explodes all nested arrays and sub-objects within the input hierarchy.
mode VARCHAR 'BOTH' Restricts explosion to 'ARRAY' elements only, 'OBJECT' keys only, or 'BOTH' .
Columns Produced by FLATTEN
Every invocation of FLATTEN outputs a deterministic virtual table with six standard columns:
seq ( INT ) : A unique sequence number corresponding to the source parent row. All exploded rows derived from the same source row share identical seq numbers.
key ( VARCHAR ) : For objects, the key name. For arrays, this is always NULL .
path ( VARCHAR ) : The full path to the element being extracted.
index ( INT ) : For arrays, the zero-based element index ( 0, 1, 2... ). For objects, this is always NULL .
value ( VARIANT ) : The unnested value of the array element or object key. Because it is returned as VARIANT , it requires casting ( value::STRING ).
this ( VARIANT ) : The parent container object or array currently being exploded.
- Comprehensive FLATTEN Example: Order Processing -- Table containing orders with an embedded array of items
- CREATE OR REPLACE TABLE raw_orders (
- order_id VARCHAR(32),
- order_date DATE,
- order_json VARIANT
- );
- - Sample Query unnesting line items
- SELECT
- r.order_id,
- r.order_date,
- f.index AS item_line_number,
- f.value:item_sku::VARCHAR(64) AS sku,
- f.value:quantity::INT AS quantity,
- f.value:price::NUMBER(10,2) AS unit_price,
- (f.value:quantity::INT * f.value:price::NUMBER(10,2)) AS line_total
- FROM raw_orders r,
- LATERAL FLATTEN(input => r.order_json:line_items, outer => TRUE) f
- WHERE f.value:price::NUMBER(10,2) > 50.00;
Exam Trap on outer => TRUE : If an order contains zero items ( line_items: [] ) or the line_items key is missing, standard LATERAL FLATTEN(input => ...) acts as an inner join and completely eliminates the order from the result set. To guarantee that orders without line items appear in reporting (e.g., cancelled or pending orders), you must specify outer => TRUE .
Sub-Column Columnarization, Automatic Pruning & Performance
One of the most impressive technical feats of Snowflake's database engine is how it optimizes VARIANT storage under the hood. While developers interact with VARIANT data using schema-on-read JSON semantics, Snowflake physically stores and scans the data using high-performance columnar execution .
Physical Sub-Column Extraction Mechanics
When semi-structured documents are ingested into a VARIANT column within a Snowflake table:
Path Frequency Analysis : Snowflake's background micro-partition write engine analyzes the structure of each ingested document across the rows belonging to that micro-partition.
Type Detection & Extraction : For paths that appear frequently and demonstrate consistent primitive data types (e.g., payload:customer.id always containing an integer, or payload:device.temp always containing a float), Snowflake automatically extracts those sub-paths into distinct physical columnar sub-columns within the micro-partition file.
Columnar Encoding & Compression : The extracted sub-columns are compressed using standard columnar compression algorithms (e.g., dictionary encoding, run-length encoding, bit-packing), identical to first-class relational columns.
Fallback Representation : Irregular, highly sparse, or deeply nested dynamic paths remain encoded in Snowflake's optimized internal binary semi-structured format within the micro-partition. Logical View (VARIANT Column): Physical Micro-Partition Storage:
- ┌──────────────────────────────────────┐ ┌──────────────────────────────────────────────┐
- │ Row 1: {"id": 10, "geo": "US"} │ │ Sub-Col: payload:id -> [10, 11, 12] (INT) │
- │ Row 2: {"id": 11, "geo": "CA"} │──►│ Sub-Col: payload:geo -> ['US','CA','US'] │
- │ Row 3: {"id": 12, "geo": "US"} │ │ Metadata: min/max per sub-column registered │
- └──────────────────────────────────────┘ └──────────────────────────────────────────────┘
- Micro-Partition Metadata & Query Pruning
Because frequently queried sub-paths are physically stored as discrete sub-columns, Snowflake's metadata layer registers min/max statistics for these sub-columns in the micro-partition header.
- When a query executes with a filter on a nested attribute: SELECT * FROM raw_events
- WHERE payload:customer.country::VARCHAR = 'DE';
The Snowflake Cloud Services query optimizer evaluates the query predicate against the min/max statistics stored for the sub-column payload:customer.country across all micro-partitions.
Micro-partitions whose sub-column min/max range does not include 'DE' are pruned entirely , without the virtual warehouse reading the micro-partitions from cloud object storage.
Furthermore, during query execution, the virtual warehouse scans only the extracted sub-column rather than parsing the entire VARIANT payload for every row.
Architectural Decision: Schema-on-Read vs. Flattened Relational Columns
While sub-column extraction provides exceptional out-of-the-box performance, enterprise architects must make deliberate physical modeling choices for high-scale analytical environments:
Architectural Factor Native VARIANT Storage Extracted Relational Table (Flattened)
Schema Flexibility Infinite. Adapts instantly to upstream JSON schema drift, new fields, and type polymorphism without DDL changes. Rigid. Requires DDL migrations ( ALTER TABLE ADD COLUMN ) and pipeline updates when schema evolves.
Storage Overhead Slightly higher due to path identifier metadata and variable structure representations. Minimal. Maximum compression achieved through pure homogeneous data types.
Query Performance Near-relational for extracted paths. Slower for irregular, sparse, or non-columnarized paths. Deterministic and maximum performance for all columns. Full support for search optimization.
Clustering Keys Direct uncast VARIANT columns cannot be used as clustering keys. Materialized expressions must be cast ( payload:id::INT ). First-class relational columns can be directly assigned to table clustering keys.
Governance & Masking Dynamic Data Masking policies on VARIANT require complex JSON manipulation or apply only to the whole blob. Granular column-level Dynamic Data Masking and Row Access Policies can be applied directly to individual fields.
Exam Best Practice Guidelines for Semi-Structured Modeling
Land Raw in VARIANT : Always ingest raw event payloads directly into an ELT landing table with a VARIANT column to ensure 100% data fidelity and avoid load failures caused by schema changes.
Materialize High-Frequency Filters and Joins : If specific nested attributes (such as tenant_id , event_date , or user_id ) are repeatedly used in JOIN conditions, WHERE clauses, or GROUP BY operations across heavy analytical queries, extract them into explicit relational columns via a transformation pipeline or defined as deterministic virtual columns.
Clustering on Semi-Structured Data : If a table must be clustered on an attribute inside a VARIANT , you must explicitly cast the expression in the clustering key definition: ALTER TABLE raw_events CLUSTER BY (payload:tenant_id::VARCHAR, payload:event_date::DATE);
Loading diagram... Semi-Structured Processing Pipeline: Ingestion, Physical Columnarization, and Lateral Flatten