Type Widening and the Variant Data Type

Estimated read: 13 min

Two schema-related capabilities that go beyond the “add a column” evolution from the previous lecture: widening a column’s type in place, and storing genuinely semi-structured data without pre-defining a rigid schema for it via the VARIANT type.

Type widening

The previous lecture showed ALTER TABLE ... ALTER COLUMN ... TYPE for changing a column’s type. Type widening specifically refers to safe, information-preserving type changes – ones where every existing value fits losslessly into the new type:

ALTER TABLE main.default.orders ALTER COLUMN quantity TYPE BIGINT;   -- INT -> BIGINT
ALTER TABLE main.default.orders ALTER COLUMN unit_price TYPE DOUBLE; -- FLOAT -> DOUBLE
ALTER TABLE main.default.orders ALTER COLUMN order_total TYPE DECIMAL(12,4); -- DECIMAL(10,2) -> DECIMAL(12,4)
Widening direction Example
Smaller integer to larger INT -> BIGINT
Lower to higher precision float FLOAT -> DOUBLE
Smaller decimal to larger scale/precision DECIMAL(10,2) -> DECIMAL(12,4)

Key term: widening is a metadata-only operation when supported – Delta doesn’t need to rewrite every existing data file, because every existing value already fits the new, larger type. This is meaningfully cheaper than a full table rewrite, and is exactly the operation you’d reach for when a legacy INT column turns out to need BIGINT range as data volume grows past what anyone originally planned for.

Narrowing (the reverse direction – BIGINT to INT, DOUBLE to FLOAT) is not supported as an in-place operation, because it can lose data – that requires an explicit rewrite with your own validation that no values are actually out of range for the smaller type.

The VARIANT type: semi-structured data, natively

Legacy warehouses generally forced a choice for JSON-shaped data: flatten it into rigid columns up front (losing flexibility if the source schema shifts), or store it as a raw string column (losing queryability – every access needs a parse). VARIANT is Databricks’ answer to that tradeoff: a native column type that stores semi-structured data efficiently while remaining directly queryable, without predefining its internal shape.

CREATE TABLE main.default.events (
    event_id BIGINT,
    payload  VARIANT
);

INSERT INTO main.default.events
SELECT 1, parse_json('{"type": "click", "meta": {"page": "/checkout", "duration_ms": 340}}');
-- Querying into a variant column
SELECT
  event_id,
  payload:type::STRING              AS event_type,
  payload:meta.page::STRING         AS page,
  payload:meta.duration_ms::INT     AS duration_ms
FROM main.default.events;

The : operator navigates into the variant, . descends into nested objects, and ::TYPE casts the extracted value – variant_get() is the equivalent function form if you prefer that syntax.

Why VARIANT over a plain JSON string column

  JSON stored as STRING VARIANT
Query without parsing No – every access needs from_json/manual parsing Yes – direct : path access
Schema flexibility Full (it’s just text) Full (still no fixed schema required)
Storage/query efficiency Poor – reparsed on every query Better – Databricks stores it in an optimized internal format
Type safety on extraction Manual ::TYPE casts, try_variant_get for safe casting

Real-world use in this guide’s context

VARIANT is the natural landing type for bronze-layer data straight from an API or event source, where the source schema isn’t fully known or stable up front – exactly the situation Lakeflow Connect’s ingestion patterns cover later in this part. Land the raw payload as VARIANT in bronze, then extract and type only the fields silver-layer transformations actually need – avoiding both the “guess the whole schema up front” problem and the “everything is an unqueryable string” problem at once.

Known limitations

VARIANT columns can’t currently be used as clustering keys, partition columns, or Z-order keys, and have some restrictions on direct comparison and grouping operations – extract the specific fields you need to filter or cluster on into their own typed columns rather than relying on the variant column itself for those operations.

For the complete official reference, including variant_explode for arrays and VariantVal’s Python API, see Query variant data.


| ← Previous: Schema Enforcement and Schema Evolution | Next: Table constraints - NOT NULL, CHECK, and Identity Columns → | |:—|—:|


Back to top

Independent, self-authored Databricks companion notes.

This site uses Just the Docs, a documentation theme for Jekyll.