~12 min read

Understanding DAX

The Language Behind Power BI

From familiar Excel formulas to powerful data models. Part 1 of the DAX Fundamentals series.

DAX Fundamentals series
Scroll to explore

What is DAX?

The formula language that powers everything in Power BI

If you've used Excel formulas, you already know more DAX than you think. DAX (Data Analysis Expressions) is the formula language behind Power BI, Analysis Services, and Power Pivot. It's a function language, so there are no loops or cell references. You compose functions together, and the engine figures out which rows to evaluate based on your data model.

Microsoft brought over 80+ functions straight from Excel (SUM, IF, AVERAGE, the ones you already know), then added 100+ DAX-only functions like CALCULATE, FILTER, and ALL. Over 250 functions total. You do not need to memorize them all (honestly, nobody has).

DAX gives you three types of calculations. The difference between them shapes everything from performance to whether your numbers come out right:

Measures

Dynamic calculations evaluated at runtime. They respond to filters, slicers, and context in your visuals. The most common calculation type in DAX.

Recommended Default

Calculated Columns

New columns added to tables, calculated row by row during refresh. Stored in memory. Use for values you'll filter or sort by.

Stored Per Row

Calculated Tables

Entirely new tables generated from DAX expressions. Perfect for date tables, bridge tables, or summarized snapshots.

Materialized Table

Why should you care? Pick the wrong calculation type and you get bloated models, sluggish visuals, or numbers that just look wrong. We'll dig into each type in the next section.

From Excel to DAX

The mental model shift that changes everything

In Excel, you point at cells. A1, B2:B10, you know the drill. DAX doesn't work that way. DAX formulas operate on columns and tables, and which rows actually get included depends entirely on context (filters, slicers, relationships). The formula stays the same; the context changes the result. This single concept is the biggest mental shift you'll make.

A lot of the functions you already know exist in both languages. Here's how they compare side by side:

Excel

=SUM(B2:B100)

Sums a specific range of cells.

vs

DAX

Total Revenue = SUM( 'Trades'[TradeTotal] )

Sums the entire column. Context determines which rows contribute.

Key Insight: No cell references. DAX sums the column, filtered by context. Select "2225" in a slicer and only 2225 rows are summed. The formula doesn't change; the context does.

Excel

=IF(A2="High",3,
  IF(A2="Medium",2,
    IF(A2="Low",1,0)))

Nested IFs get messy fast.

vs

DAX

Priority Score =
SWITCH(
  TRUE(),
  [Priority] = "High", 3,
  [Priority] = "Medium", 2,
  [Priority] = "Low", 1,
  0
)

SWITCH(TRUE(), ...) is clean and scales to any number of conditions.

Key Insight: SWITCH replaces nested IF chains. The SWITCH(TRUE(), ...) pattern evaluates conditions in order and returns the first match. So much cleaner.

Excel

=VLOOKUP(A2, Cargo, 3, FALSE)

Manual lookup by column index.

vs

DAX

-- Relationships handle this!
-- Just use: 'Cargo'[Class]

The data model's relationships replace manual lookups entirely.

If your tables are properly related, you can reference columns directly. RELATED() exists for edge cases, but the model usually handles it for you.

Excel

=AVERAGE(B2:B100)

Averages a fixed range. One level of aggregation.

vs

DAX

Avg Daily Revenue =
AVERAGEX(
  VALUES( 'Stardate'[Date] ),
  [Total Revenue]
)

Averages Total Revenue per unique date. You control the grain.

Key Insight: Iterator functions like AVERAGEX evaluate row-by-row over a table, then aggregate the results. Excel simply cannot do this in a single formula.

Excel

=COUNTA(A2:A100)
=COUNTIF(B2:B100, "Minerals")

COUNT for numbers, COUNTA for non-blanks, COUNTIF for conditions.

vs

DAX

Trade Count = COUNTROWS( 'Trades' )

Mineral Trades =
CALCULATE(
  COUNTROWS( 'Trades' ),
  'Cargo'[Class] = "Minerals"
)

COUNTROWS counts table rows; CALCULATE adds filter conditions.

Pro tip: Prefer COUNTROWS over COUNT in DAX. COUNT only works on columns; COUNTROWS counts table rows in the current filter context, which is much more flexible.

Excel

=A2 & " - " & B2
=TEXTJOIN(", ", TRUE, A2:A10)

Ampersand for joining cells; TEXTJOIN for ranges.

vs

DAX

Cargo Label =
'Cargo'[Class] & " - " & 'Cargo'[Name]

Class List =
CONCATENATEX(
  VALUES( 'Cargo'[Class] ),
  'Cargo'[Class], ", "
)

Ampersand works the same; CONCATENATEX iterates and joins.

Pro tip: CONCATENATEX is DAX's TEXTJOIN equivalent. It iterates over a table and joins text with a delimiter. Super useful for creating dynamic labels in measures.

Excel

=YEAR(A2)
=EOMONTH(A2, 0)
=DATEDIF(A2, TODAY(), "M")

Individual date functions applied per cell.

vs

DAX

Revenue YTD =
TOTALYTD( [Total Revenue], 'Stardate'[Date] )

Prior Cycle =
CALCULATE(
  [Total Revenue],
  DATEADD( 'Stardate'[Date], -1, YEAR )
)

Built-in time intelligence: YTD, prior period, rolling windows.

Key Insight: DAX has an entire family of time intelligence functions that make year-over-year, quarter-to-date, and rolling-average calculations trivial. Excel has nothing even close.

Calculation Types

Measures, calculated columns, and calculated tables, and when to use each

You have three core tools in DAX: measures, calculated columns, and calculated tables. Each one is built for different scenarios, and picking the right one directly impacts performance, memory usage, and whether your report actually behaves the way you expect. Get it wrong and you end up with a model that's slow, bloated, or just confusing. So let's break each one down.

Measures

The workhorse of DAX. Evaluated on the fly every time a visual renders. The result depends entirely on the filter context.

Profit Margin = DIVIDE( [Net Profit], [Total Revenue] )
Recommended Default

Calculated Columns

New columns evaluated row by row during data refresh. Stored in memory. Use when you need values to filter, sort, or slice by.

Value Tier = IF( 'Cargo'[Value] > 100, "Premium", "Standard" )
Uses Memory Per Row

Calculated Tables

Entirely new tables from DAX expressions. Materialized during refresh. Use for date tables, bridge tables, or distinct value lists.

Stardate = CALENDARAUTO()
Structural / Niche

Key Insight: When in doubt, use a measure. Seriously. Measures do not consume per-row memory, they respond dynamically to whatever the user selects, and they're the right choice roughly 99% of the time. Only reach for calculated columns or calculated tables when you have a specific structural reason to.

Still not sure? Walk through this:

Which Calculation Type Should You Use?

Do you need a value for each row in a table?

Will you use this value to filter, sort, or slice a visual?

Will this aggregate be used in report visuals?

Is this a helper table, bridge table, or date table?

Measure

Use a Measure

Measures are what you'll write most often in DAX. They evaluate at runtime based on filter context, making them perfect for KPIs, totals, ratios, and any value that changes based on what the user is looking at.

Rule of thumb: About 99% of the time, you want a Measure. Calculated columns are tempting because you can see the data row by row, but measures are almost always the better choice.

Calculated Column

Use a Calculated Column

When you need a row-level value that users can filter or sort by (like a category label, age bucket, or profit tier), a calculated column is the right choice. Just remember: it is stored in memory for every row.

Consider a Measure

A Measure Might Still Work

If you just need to display a calculated value (not filter by it), a measure is usually more efficient. Measures don't consume memory per row and respond dynamically to context.

Calculated Table

Use a Calculated Table

Calculated tables are perfect for bridge tables, date tables, or summarized reference data. They're materialized (stored in memory), so use them sparingly for specific structural needs.

Syntax and References

How to read and write DAX: the building blocks of every formula

Reference Types

DAX syntax has three types of references. Bookmark this one:

'TableName' Single quotes for tables (required if name has spaces)
'Table'[Column] Brackets for columns. Always include the table prefix
[MeasureName] Brackets only for measures. No table prefix needed
Operators
+ - * / Arithmetic operators
= <> < > <= >= Comparison operators
&& || Logical AND, logical OR
& Text concatenation
IN { } Multiple OR conditions (e.g., [Origin] IN {"Andara", "Cobalt"})

You do not need to memorize all 250+ functions. But knowing the categories exist means you'll know where to look when you need something:

Aggregation Functions

SUM AVERAGE COUNT DISTINCTCOUNT MIN MAX
Total Revenue = SUM( 'Trades'[TradeTotal] )

Filter Functions

CALCULATE FILTER ALL ALLEXCEPT VALUES SELECTEDVALUE
Revenue All Cargo =
CALCULATE( [Total Revenue], ALL( 'Cargo' ) )

Logic Functions

IF SWITCH AND OR TRUE FALSE
Status =
IF( [Profit Margin] > 0.2, "Healthy", "At Risk" )

Iterator Functions

SUMX AVERAGEX COUNTX MINX MAXX RANKX
Total Credits =
SUMX( 'Trades', 'Trades'[Units] * 'Trades'[UnitCost] )

Time Intelligence

DATESYTD DATESQTD DATEADD DATESBETWEEN TOTALYTD
Revenue YTD =
TOTALYTD( [Total Revenue], 'Stardate'[Date] )

Text and Info

CONCATENATE FORMAT LEFT RIGHT UPPER LOWER
Revenue Display =
FORMAT( [Total Revenue], "$#,##0" )

The Data Model

Why the structure of your data matters more than any formula you'll write

Here's something that trips people up early: DAX is only as good as the data model underneath it. You can write perfect formulas and still get wrong answers if the model is messy. The recommended structure is called a star schema, and it's built from two types of tables:

Fact Tables

The center of your model. Transactional data: orders, sales, events. Each row is something that happened. Contains foreign keys and numeric columns that measures aggregate.

Events & Transactions

Dimension Tables

The "who, what, when, where" context. Cargo, Merchants, Stardate. Each has a unique key and descriptive attributes users filter and slice by.

Descriptive Context

Picture it like a hub and spokes. The fact table sits in the center, with dimension tables radiating outward. They're connected by one-to-many relationships, meaning each dimension row (1) can match many fact rows (*):

Dimension

Cargo

  • CargoKey (PK)
  • CargoName
  • Class
  • Value
Dimension

Stardate

  • StardateKey (PK)
  • Cycle
  • Quarter
  • Month
Dimension

Merchants

  • MerchantKey (PK)
  • MerchantName
  • Sector
  • Segment
1 1 1 * * *
Fact Table

Trades

  • TradeKey (PK)
  • CargoKey (FK)
  • StardateKey (FK)
  • MerchantKey (FK)
  • Units
  • TradeTotal

Why This Matters for DAX: Star schema enables filter propagation. When someone selects "Minerals" in a slicer, the filter flows from the Cargo dimension, down the relationship, into the Trades fact table. Only Mineral rows survive. This automatic flow is what makes your measures work without any extra code.

Filter Flow

See how filters propagate through the star schema in real time

When someone picks "Minerals" in a slicer, the filter hits Cargo first, then flows down the relationship to Trades. Only matching rows survive. Every measure evaluates against that filtered subset. Try it yourself; click around and watch the numbers change:

Cargo Class

Cycle

Cargo (Dimension)
Stardate (Dimension)
Trades (Fact)
Rows in Context
20 / 20
filtered rows
Total Units
4,500
SUM( 'Trades'[Units] )
Total Revenue
$4,972,000
SUM( 'Trades'[Credits] )

This is honestly the thing that makes DAX click for most people. The measure Total Revenue = SUM('Trades'[Credits]) never changes. The formula is identical every time. But the result changes because the filter context changed. Once you internalize this, everything else in DAX starts to make sense. That's exactly what Part 2 is about: How DAX Thinks: Context is Everything.

Key Takeaways

What you've learned in Part 1

250+ Functions, Zero Cell Refs

DAX is a function language. 80+ functions come from Excel; 100+ are unique to DAX.

Context Drives Results

DAX operates on columns and tables, not cell references. Context determines which rows contribute to the result.

Measures Are Your Default

Measures are evaluated at runtime and should be your default choice roughly 99% of the time.

Columns Store, Tables Structure

Calculated columns store row-level values; calculated tables create new structures in the model.

Star Schema Is the Foundation

Fact tables surrounded by dimension tables is the architecture that makes DAX work effectively.

Filters Flow Automatically

Filters propagate from dimensions to fact tables through relationships without any extra code.

Next up: filter context and row context, how CALCULATE works, and why understanding context is the key to writing DAX that behaves the way you expect.

Continue to Part 2

How DAX Thinks: Context is Everything

Filter context, row context, CALCULATE, and context transition. The concepts that separate beginners from experts.

Part 2 of 3
1 / 8

Want hands-on help building DAX skills across your team?

We deliver DAX training tailored to your semantic models, so your team learns with the data they actually work with.

Schedule a Call Our Offerings

Discussion

Loading comments...
**On screen:** AE logo, "Understanding **DAX**: The Language Behind Power BI," ~12 min read badge, three-part series nav with Part 1 highlighted. - Welcome to Part 1 of the DAX Fundamentals series - Goal today: cover the language itself before context and patterns - Part 2 covers context and CALCULATE; Part 3 covers patterns *Transition:* "Let's start with what DAX actually is."
**On screen:** Section header "What is DAX?" Two paragraphs about the language origin and function count. - DAX powers Power BI, Analysis Services, and Power Pivot (same engine underneath) - It's a **function language**: no loops, no cell references; you compose functions - 80+ functions came straight from Excel (SUM, IF, AVERAGE) - 100+ are DAX-only (CALCULATE, FILTER, ALL) - **250+ total**, nobody memorizes them all *Transition:* "There are three types of calculations. Picking the wrong one is one of the most common mistakes."
**On screen:** Orange "Measures" card with calculator icon. Tag: "Recommended Default." - Dynamic, evaluated at runtime when a visual renders - Respond to filters, slicers, and visual context automatically - Same formula returns different numbers depending on what the user clicks - This is what you'll write 99% of the time
**On screen:** Teal "Calculated Columns" card with table-and-highlighted-column icon. Tag: "Stored Per Row." - New columns added to existing tables - Calculated **row by row during refresh**, then stored in memory - Cost: every row consumes RAM, forever - Use when you need to filter, sort, or slice by the value
**On screen:** Blue "Calculated Tables" card with two-overlapping-tables icon. Tag: "Materialized Table." - Entirely new tables, generated from a DAX expression - Materialized at refresh (lives in memory like any other table) - Typical use cases: date tables, bridge tables, summarized snapshots - Niche tool, not an everyday thing
**On screen:** Orange callout: "Why should you care?" - Pick the wrong type and you get bloat, slow visuals, or wrong numbers - The next section breaks down each type with examples and a decision flow *Transition:* "Before we get into types, let's bridge from Excel. Most of you came from Excel and that's the easiest mental on-ramp."
**On screen:** Section header "From Excel to DAX." Paragraph about the mental shift. - In Excel you point at **cells**: A1, B2:B10 - DAX doesn't work that way; it operates on **columns and tables** - Which rows get included depends entirely on **context** (filters, slicers, relationships) - Same formula, different context, different result - This is the single biggest shift coming from Excel *Transition:* "The good news is most of the function names carry over. Let's compare them side by side."
**On screen:** Tabbed comparison: SUM, IF/SWITCH, Lookups, AVERAGE vs AVERAGEX, COUNT vs COUNTROWS, Text Concat, Date Functions. Click each tab. - **SUM:** Excel sums a range (`B2:B100`); DAX sums an entire column (`SUM('Trades'[TradeTotal])`). Context decides which rows count. - **IF / SWITCH:** Nested IFs get ugly fast. The `SWITCH(TRUE(), ...)` pattern is the clean replacement. - **Lookups:** VLOOKUP is gone. Relationships handle this; reference the related column directly. RELATED() exists for edge cases. - **AVERAGE vs AVERAGEX:** AVERAGE averages one column. AVERAGEX iterates a table and averages an expression per row. Iterators are the X family. - **COUNT vs COUNTROWS:** Prefer COUNTROWS. COUNT only works on columns; COUNTROWS counts table rows in the current filter context. - **Text concat:** `&` works the same. CONCATENATEX is the DAX TEXTJOIN: iterate a table, join with a delimiter. - **Dates:** DAX has a whole time intelligence family (TOTALYTD, DATEADD, DATESBETWEEN). Excel has nothing close. *Transition:* "Now back to those three calculation types. Let's dig into each one properly."
**On screen:** Section header "Calculation Types." Subhead: "Measures, calculated columns, and calculated tables, and when to use each." - Three tools: **measures**, **calculated columns**, **calculated tables** - Wrong choice means slow, bloated, or confusing reports - We'll go through each, then a decision flowchart for the ambiguous cases *Transition:* "Start with measures, because they're the answer most of the time."
**On screen:** Orange Measures card with calculator icon. Code sample: `Profit Margin = DIVIDE([Net Profit], [Total Revenue])`. Tag: "Recommended Default." - The **workhorse** of DAX - Evaluated on the fly every time a visual renders - Result depends entirely on filter context - Zero per-row memory cost - DIVIDE is the safe divide function (handles divide-by-zero cleanly)
**On screen:** Teal Calculated Columns card with table-and-column icon. Code sample: `Value Tier = IF('Cargo'[Value] > 100, "Premium", "Standard")`. Tag: "Uses Memory Per Row." - New columns on existing tables, evaluated **row by row at refresh** - Stored in memory; every row of the table grows the model - Use when you need to filter, sort, or slice by the value - A tier label, an age bucket, a flag: good calculated column candidates
**On screen:** Blue Calculated Tables card. Code sample: `Stardate = CALENDARAUTO()`. Tag: "Structural / Niche." - Brand new tables built from a DAX expression - Materialized at refresh - CALENDARAUTO is the most common example: creates a date table covering your fact data's range - Other uses: bridge tables for many-to-many, summarized reference snapshots - Specific structural choice; not an everyday move
**On screen:** Orange callout: "Key Insight: When in doubt, use a measure." - Measures: no per-row memory, dynamic to user selections, right answer about **99%** of the time - Only reach for calculated columns or tables when there's a **specific structural reason** - "It's easier to see the data row by row" is not a good reason
**On screen:** Interactive flowchart titled "Which Calculation Type Should You Use?" Click through the questions. - Q1: "Need a value per row?" Yes goes to row-level path; No goes to aggregate path. - Q2 (row-level): "Will you filter, sort, or slice by it?" Yes -> **Calculated Column**. No -> a measure might still work. - Q3 (aggregate): "Used in visuals?" Yes -> **Measure**. No -> "I need a new table" -> Q4. - Q4: "Bridge / date / helper table?" Yes -> **Calculated Table**. Not sure -> probably a measure. - Walk through one path live so they see the logic *Transition:* "Once you've picked the calculation type, you need the syntax to actually write it. Let's look at how DAX is structured."
**On screen:** Section header "Syntax and References." Subhead: "How to read and write DAX." - Two cheat sheets coming: **reference types** and **operators** - Then a function explorer covering the six main function categories - This is the section to bookmark; people refer back to it for weeks *Transition:* "Reference types first, because mixing these up is the most common typo in DAX."
**On screen:** Cheat sheet titled "Reference Types" with three rows. - `'TableName'` (single quotes) for **tables**; quotes are required when the name has spaces - `'Table'[Column]` for **columns**; always include the table prefix - `[MeasureName]` for **measures**; brackets only, no table prefix - The table-prefix rule for columns prevents ambiguity and makes the code skim faster - Quick reminder: tables get quotes, columns/measures get brackets
**On screen:** Cheat sheet titled "Operators" with five rows. - Arithmetic: `+ - * /` - Comparison: `= <> < > <= >=` (note: `<>` not `!=`) - Logical: `&&` AND, `||` OR - Text concatenation: `&` (same as Excel) - `IN { }` for multiple OR conditions: `[Origin] IN {"Andara", "Cobalt"}` is cleaner than chained ORs
**On screen:** Function explorer tabs: Aggregation, Filter, Logic, Iterator, Time Intelligence, Text & Info. Click each. - **Aggregation:** SUM, AVERAGE, COUNT, DISTINCTCOUNT, MIN, MAX. The everyday verbs. - **Filter:** CALCULATE, FILTER, ALL, ALLEXCEPT, VALUES, SELECTEDVALUE. The context-shaping functions; this is what Part 2 is about. - **Logic:** IF, SWITCH, AND, OR, TRUE, FALSE. SWITCH is the one to lean on. - **Iterator (X functions):** SUMX, AVERAGEX, COUNTX, MINX, MAXX, RANKX. They iterate a table row by row, then aggregate. Powerful and easy to overuse. - **Time Intelligence:** DATESYTD, DATESQTD, DATEADD, DATESBETWEEN, TOTALYTD. All require a proper date table. - **Text & Info:** CONCATENATE, FORMAT, LEFT, RIGHT, UPPER, LOWER. Standard text work. - Don't memorize. Know the categories so you know where to look. *Transition:* "Now the part people skip: the model itself. The best DAX in the world can't fix a bad model."
**On screen:** Section header "The Data Model." Subhead about structure mattering more than formulas. - Critical point: **DAX is only as good as the data model underneath it** - Perfect formulas + messy model = wrong answers - The recommended structure is the **star schema** - Two table types: facts and dimensions *Transition:* "Fact tables first."
**On screen:** Orange Fact Tables card. Tag: "Events & Transactions." - The **center** of the model - **Transactional**: each row is something that happened (an order, a sale, a trade) - Holds foreign keys (pointing to dimensions) and the numeric columns measures aggregate - In the example schema: the Trades table
**On screen:** Teal Dimension Tables card. Tag: "Descriptive Context." - The **who / what / when / where** context - Each dimension has a **unique key** and the descriptive attributes users filter and slice by - In the example schema: Cargo, Merchants, Stardate - These are what fill slicers, axes, and row/column headers in visuals
**On screen:** Animated star schema diagram. Cargo / Stardate / Merchants on top as dimensions; Trades on the bottom as the fact. Dashed lines with arrows show 1-to-many relationships (1 on the dim side, * on the fact side). - **Hub and spokes**: fact in the center, dimensions radiating outward - Connected by **one-to-many** relationships - 1 on the dimension side, * (many) on the fact side - The arrows show **filter direction**: from dimensions down into the fact - Primary keys on the dimension side, foreign keys on the fact side
**On screen:** Orange callout: "Why This Matters for DAX." - Star schema enables **filter propagation** - User picks "Minerals" in a slicer -> filter hits Cargo -> flows down the relationship -> only Mineral rows survive in Trades - Every measure on the page then evaluates against that filtered subset - This automatic flow is what lets measures be so short; the model does the work *Transition:* "Let's watch it actually happen with a live demo."
**On screen:** Section header "Filter Flow." Subhead: "See how filters propagate through the star schema in real time." - Recap the mechanic: dimension filter -> relationship -> fact table -> measures - The demo below makes this visible: pick a Cargo Class or a Cycle, watch rows disappear, watch the metrics shift *Transition:* "Click around and watch the numbers move."
**On screen:** Interactive demo with two slicer groups (Cargo Class, Cycle), the three-table schema diagram, and three metric cards (Rows in Context, Total Units, Total Revenue). - Default state: All / All, 20 of 20 rows in context - Click **Minerals** -> Cargo table dims to mineral rows only; Trades dims to matching rows; metrics drop - Click **2225** on top of that -> further reduces Trades rows - Point out: the measure formulas (`SUM('Trades'[Units])`, `SUM('Trades'[Credits])`) are **identical** the whole time - Only the filter context changed; the formulas never did - Reset to All/All before moving on so the next slide starts clean
**On screen:** Orange callout. Code line: `Total Revenue = SUM('Trades'[Credits])`. Reference to Part 2. - This is the moment DAX clicks for most people - The measure formula **never changes** - The result changes because **context changes** - Once that lands, everything else makes sense - Perfect setup for Part 2: "How DAX Thinks: Context is Everything" *Transition:* "Quick recap before we close out."
**On screen:** Section header "Key Takeaways." Subhead: "What you've learned in Part 1." - Six takeaway cards coming, then the bridge to Part 2 - Keep this section quick; the cards are reinforcement, not new material *Transition:* "Six takeaways, one card at a time."
**On screen:** Card 1: "250+ Functions, Zero Cell Refs." - DAX is a **function language** - 80+ functions from Excel + 100+ DAX-only = 250+ total - No cell references; you compose functions
**On screen:** Card 2: "Context Drives Results." - DAX works on **columns and tables**, not cells - **Context** decides which rows contribute - Same formula, different context, different answer - This is the bridge to Part 2
**On screen:** Card 3: "Measures Are Your Default." - Measures evaluate at runtime, no per-row memory - About **99%** of what you write should be a measure - Reach for columns or tables only when there's a specific structural reason
**On screen:** Card 4: "Columns Store, Tables Structure." - **Calculated columns** store row-level values (memory cost per row) - **Calculated tables** create new structures (date tables, bridge tables) - Different jobs, different costs
**On screen:** Card 5: "Star Schema Is the Foundation." - Fact in the center, dimensions around it, one-to-many relationships - The architecture that makes DAX work cleanly - If the model is messy, no amount of DAX cleverness fixes it
**On screen:** Card 6: "Filters Flow Automatically." - Filters propagate from dimensions to fact tables through relationships - No extra code required - This is why measures stay short
**On screen:** "Continue to Part 2" card linking to "How DAX Thinks: Context is Everything." Badge: "Part 2 of 3." - Part 1 was the language; Part 2 is the brain - Filter context, row context, CALCULATE, context transition: the four concepts that separate beginners from experts - If anything in this guide felt hand-wavy ("context decides which rows contribute"), Part 2 is where it gets concrete *Transition:* "Questions before we close?"