🎉 75% of content is free forever — Unlock Premium from $10/mo →
CW
đŸ’ŧ Servicesâ„šī¸ Aboutâœ‰ī¸ ContactView Pricing Plansfrom $10

Snowflake Slowly Changing Dimensions (SCD)

đŸŸĸ Free Lesson

Advertisement

Snowflake Slowly Changing Dimensions (SCD)

Slowly Changing Dimensions (SCD) techniques manage how historical data is tracked when dimension attributes change over time.

SCD Types OverviewSourceChangesSCD Type 1OverwriteSCD Type 2VersioningSCD Type 3History ColsDimensionTableType 1: No HistoryUPDATE SET col = new_valueType 2: Full HistoryINSERT new, UPDATE is_currentType 3: Limited HistoryUPDATE prev_col, curr_col
SCD Type 2: Versioning TimelineVersion 1Version 2Version 3Currentvalid_from: Jan 1valid_to: Mar 15is_current: FALSEvalid_from: Mar 15valid_to: Jun 20is_current: FALSEvalid_from: Jun 20valid_to: NULLis_current: TRUESCD Type 2 Columnssurrogate_key (PK)business_key (NK)valid_from, valid_tois_current (flag)hash_diff (change detection)

SCD Type Definitions

SCD Type 1: Overwrite

SCD Type 1 is the simplest approach. Changes overwrite existing data without preserving history. This is ideal for correcting data errors or when only the current state matters.

Use Cases:

  • Correction of data entry mistakes
  • Attributes where history is irrelevant (e.g., phone number, email)
  • Small dimension tables where storage is a concern
  • Real-time dashboards showing only current state

Implementation with MERGE:

MERGE INTO dim_customer AS target
USING staging_customer AS source
ON target.customer_id = source.customer_id
WHEN MATCHED AND source.name <> target.name
    OR source.email <> target.email
    OR source.phone <> target.phone THEN
    UPDATE SET
        target.name = source.name,
        target.email = source.email,
        target.phone = source.phone,
        target.modified_at = CURRENT_TIMESTAMP()
WHEN NOT MATCHED THEN
    INSERT (customer_id, name, email, phone, created_at)
    VALUES (source.customer_id, source.name, source.email, source.phone, CURRENT_TIMESTAMP());

SCD Type 2: Versioning

SCD Type 2 provides full historical tracking. Each change creates a new version of the record. This is the most commonly used SCD type for analytical workloads.

Use Cases:

  • Regulatory compliance requiring full audit trails
  • Historical trend analysis and reporting
  • Fact table foreign key integrity across time periods
  • Customer dimension tracking for CRM analytics

Implementation with valid_from/valid_to:

CREATE OR REPLACE PROCEDURE sp_scd_type2_merge(p_table_name VARCHAR, p_stage_name VARCHAR)
RETURNS VARCHAR
LANGUAGE SQL
AS
$$
BEGIN
    MERGE INTO dim_customer AS target
    USING staging_customer AS source
    ON target.customer_id = source.customer_id
        AND target.is_current = TRUE
    WHEN MATCHED AND
        HASH(target.name, target.email, target.region) <>
        HASH(source.name, source.email, source.region) THEN
        UPDATE SET
            target.valid_to = CURRENT_TIMESTAMP(),
            target.is_current = FALSE
    WHEN NOT MATCHED THEN
        INSERT (customer_id, name, email, region, valid_from, valid_to, is_current)
        VALUES (
            source.customer_id,
            source.name,
            source.email,
            source.region,
            CURRENT_TIMESTAMP(),
            NULL,
            TRUE
        );
END;
$$;

Querying Current Records:

SELECT customer_id, name, email, region
FROM dim_customer
WHERE is_current = TRUE;

Querying Historical Records:

SELECT customer_id, name, email, region,
       valid_from, valid_to,
       DATEDIFF(day, valid_from, COALESCE(valid_to, CURRENT_TIMESTAMP())) AS days_active
FROM dim_customer
WHERE customer_id = 'CUST-001'
ORDER BY valid_from;

SCD Type 3: Previous/Current Columns

SCD Type 3 maintains a fixed number of previous values through dedicated columns. This provides limited history without the row proliferation of Type 2.

Use Cases:

  • When only the immediate previous value is needed
  • Low-change dimensions (e.g., region, department)
  • Performance-sensitive queries requiring minimal row counts
  • Simple reporting scenarios

Implementation with previous and current columns:

MERGE INTO dim_employee AS target
USING staging_employee AS source
ON target.employee_id = source.employee_id
WHEN MATCHED AND source.department <> target.current_department THEN
    UPDATE SET
        target.previous_department = target.current_department,
        target.current_department = source.department,
        target.dept_changed_at = CURRENT_TIMESTAMP()
WHEN NOT MATCHED THEN
    INSERT (employee_id, name, previous_department, current_department, dept_changed_at)
    VALUES (
        source.employee_id,
        source.name,
        NULL,
        source.department,
        CURRENT_TIMESTAMP()
    );

Comparison: When to Use Each Type

FactorSCD Type 1SCD Type 2SCD Type 3
History RetainedNoneFullLimited (1-2 changes)
Storage ImpactMinimalHigh (row duplication)Moderate
Query ComplexitySimpleModerate (filters needed)Simple
Use WhenOnly current state mattersFull audit trail requiredLimited history is sufficient
Row CountSame as sourceGrows with changesSame as source
PerformanceBest (fewest rows)Moderate (many rows)Good
Common ScenarioData correctionRegulatory complianceSimple tracking

Real-World Example: Customer Address Changes

See Also

Need Expert Snowflake Help?

Get personalized tutoring, project support, or professional consulting.

Advertisement