X
Excel Zero to Hero Interactive Learning Platform
0%
πŸ“ 02_Notes/09_Data_Modeling_and_DAX/03_DAX_Fundamentals_Calculated_Columns_vs_Measures.md
View Note on GitHub β†— | Open in Obsidian
Module 9

Calculated Columns vs Measures & Dual Engine Architecture

advanced
⏱️ ~8 min read
Practice in Labs β†’

Lesson 9.3: DAX Fundamentals: Calculated Columns vs DAX Measures

🎯Learning Objective

Distinguish row context from filter context, evaluate the performance impact on the VertiPaq database, and understand how the Formula Engine (FE) and Storage Engine (SE) collaborate during query execution.

πŸŽ₯ Video Chapter: Chapter 9 – Data Modeling, Power Pivot & DAX (5:03:39)
πŸ“– Architecture Reference: The Formula Engine and Storage Engine (Endjin) & Inside the VertiPaq Engine (SQLBI)


1. Critical Differences: Calculated Columns vs DAX Measures

Every calculation in Power Pivot or Power BI belongs to one of two categories:

Architectural Attribute Calculated Column Explicit DAX Measure
Evaluation Context Row Context (evaluated row-by-row during data refresh) Filter Context (evaluated dynamically at query time)
Storage Location Physically written into the VertiPaq table structure in RAM and disk Stored as a formula definition; consumes 0 bytes of RAM at rest
RAM Impact High (creates a new column dictionary and encoded data vector) Zero (computed dynamically on CPU demand)
Recalculation Trigger When the data model is refreshed Whenever a user slices, filters, or pivots the report
Primary Usage Slicers, Column/Row headers in PivotTables The Values area ( $\Sigma$ ) of PivotTables, KPI cards, Charts
Demo Formula Example DurationMin = [DurationSec] / 60 [Sum of Sales] = SUM('Fact_ Order'[Sales])
DIAGRAM
Rendering architecture diagram...
View Mermaid Syntax
flowchart TD
    subgraph CC ["Calculated Column (Row Context)"]
        CC1["Evaluated during ETL / Data Refresh"] --> CC2["Stores Result in Table Column"]
        CC2 --> CC3["Consumes In-Memory RAM"]
        CC3 --> CC4["Can be used in Slicers & Rows"]
    end

    subgraph M ["DAX Measure (Filter Context)"]
        M1["Evaluated on Query / User Click"] --> M2["Zero Memory Footprint at Rest"]
        M2 --> M3["Computes dynamically via SE & FE"]
        M3 --> M4["Placed in Values Area & Visual Cards"]
    end

    style CC fill:#fef2f2,stroke:#ef4444,stroke-width:2px
    style M fill:#f0fdf4,stroke:#22c55e,stroke-width:2px

2. The Dual Engine Architecture: Formula Engine vs Storage Engine

When a DAX query executes (e.g., when you drag a measure into a PivotTable or click a slicer), two distinct internal engines execute the request:

DIAGRAM
Rendering architecture diagram...
View Mermaid Syntax
sequenceDiagram
    autonumber
    actor User as User / PivotTable
    participant FE as Formula Engine (FE)
    participant SE as Storage Engine (SE / VertiPaq)

    User->>FE: Requests Pivot Table Cells ([Sum of Sales] by Month)
    Note over FE: Single-Threaded<br/>Parses DAX Query Tree<br/>Identifies Filter Context
    FE->>SE: Sends xmSQL Scan Request
    Note over SE: Multi-Threaded<br/>Scans In-Memory Column Vectors<br/>Aggregates & Joins in RAM
    SE-->>FE: Returns Compressed Datacache
    Note over FE: Applies Complex Logic,<br/>Iterators & Formatting
    FE-->>User: Delivers Final Visual Grid Results

1. The Formula Engine (FE)

  • Characteristics: Single-threaded, extremely powerful, can execute any complex logic, text manipulation, and DAX expression.
  • Responsibilities:
    • Parses the incoming DAX query and produces an optimized execution tree.
    • Translates filter context into requests for the Storage Engine.
    • Handles complex iterative functions (SUMX, RANKX, CALCULATE context transitions).
    • Performs final math and formatting before returning values to Excel.

2. The Storage Engine (SE / VertiPaq)

  • Characteristics: Multi-threaded, blazingly fast, operates directly on compressed in-memory columnar caches.
  • Responsibilities:
    • Receives requests formatted in xmSQL from the Formula Engine.
    • Performs column scans, basic filters (Fact_Orders[Region] = "West"), and standard aggregations (SUM, MIN, MAX, COUNT).
    • Executes relationship joins across tables in memory.
    • Returns raw, aggregated datacaches back to the Formula Engine.

The Materialization Trap (Performance Warning!)

When a calculation cannot be resolved inside the Storage Engine (e.g., poorly written nested iterators or complex cross-table row contexts), the Storage Engine is forced to materialize millions of uncompressed rows and dump them into the single-threaded Formula Engine.

  • Result: The query stalls, CPU cores sit idle, and memory spikes.
  • The BI Developer Goal: Always write DAX measures that allow the Storage Engine to do 95%+ of the heavy lifting!

3. The Golden Rules of DAX

πŸ’‘Golden Rule #1: Never Use a Calculated Column When a Measure Suffices

If you need to calculate an aggregation (sum, average, ratio, percentage), always write an explicit DAX measure. Only use a calculated column if you must place the result onto a Slicer or a Row/Column Header.

πŸ’‘Golden Rule #2: Explicit Measures Over Implicit Measures

In Excel, dragging a numeric field directly into the Values area creates an implicit measure (e.g., Sum of Sales). Never rely on implicit measures! Always author an explicit DAX measure (Total Sales := SUM(Fact_Orders[Sales])). Explicit measures can be referenced by downstream measures, formatted consistently, and optimized.

πŸ’‘Golden Rule #3: Separate Metrics from KPIs
  • Metric (Important Number): [Sum of Sales] = SUM('Fact_ Order'[Sales])
  • KPI (Actionable Indicator):
    DAX
    Sales vs Target % := DIVIDE([Sum of Sales], [Target Sales], 0)
    

4. Live Synchronization: Module_9_Demo.xlsx

In the companion file Module_9_Demo.xlsx, the Power Pivot data model implements these explicit DAX measures:

Measure 1: Total Sales

DAX
-- Explicit Measure created on 'Fact_ Order'
[Sum of Sales] := SUM('Fact_ Order'[Sales])
  • Engine Execution: Fully pushed to the Storage Engine (SE). VertiPaq scans the compressed Sales column vector and computes the sum in sub-milliseconds across all years (2014–2017: $2,297,200.86).

Measure 2: Average Order Sales

DAX
-- Explicit Measure created on 'Fact_ Order'
[Avg Sales] := AVERAGE('Fact_ Order'[Sales])
  • Engine Execution: Executed in the Storage Engine, returning the average order ticket size ($229.86 overall).
  • Pivot Usage:
    • Sheet1: [Sum of Sales] evaluated across Dim_Date[Month] and Dim_Date[Year].
    • Sheet2: Side-by-side comparison of [Sum of Sales] and [Avg Sales] grouped by Dim_Date[Month].

5. Related Knowledge & Next Steps

πŸ”— Connected Knowledge & Hands-On Practice

8-Step Learning Loop
🎯 Practice Lab
AI-Assisted Analysis Workflow
πŸ“Š Recommended Dataset
PwC Call Center Performance (5,000 records)
⚑ Key Functions
🧠 Visual Architecture
Explore Module 9 in Mind Map β†’