
Complete DAX guide with real-world examples: IF syntax, nested IF, AND/OR conditions, SWITCH vs IF, IFERROR, ISBLANK, calculated columns vs measures, and performance optimization.
How do you write an IF statement in Power BI?
The IF function in Power BI DAX uses the syntax IF(logical_test, value_if_true, value_if_false). It evaluates a condition and returns one value when TRUE and another when FALSE. For example, IF([Sales] > 10000, "High", "Low") categorizes each row based on sales amount. IF works in calculated columns, measures, and calculated tables across Power BI Desktop, Power BI Service, and Azure Analysis Services.
The IF function is the most fundamental conditional logic function in DAX (Data Analysis Expressions). It is the building block for every decision-making formula in Power BI — from simple categorizations to complex business rules spanning dozens of conditions. Every Power BI implementation EPC Group delivers to Fortune 500 clients uses IF extensively across measures, calculated columns, and KPI definitions.
Unlike Excel's IF function, the DAX IF operates within the context of a data model. In a calculated column, IF evaluates once per row during data refresh. In a measure, IF evaluates dynamically based on the current filter context — meaning the same IF formula can return different results depending on which slicers, filters, and cross-highlights are active. Understanding this distinction is critical for writing correct DAX.
This guide covers every IF pattern you will encounter in enterprise Power BI development: basic syntax, nested IF, compound conditions with AND/OR, when to use SWITCH instead, IF with CALCULATE for conditional aggregation, error handling with IFERROR and ISBLANK, and performance optimization techniques used in production models with 50M+ rows.
The IF function takes three arguments: a logical test that evaluates to TRUE or FALSE, a value to return when TRUE, and an optional value to return when FALSE. If the third argument is omitted, IF returns BLANK() for false conditions.
// Basic IF Syntax
IF(
<logical_test>,
<value_if_true>,
<value_if_false> -- optional, returns BLANK() if omitted
)Here are practical examples that demonstrate the most common IF patterns in enterprise Power BI reports:
// Example 1: Categorize orders by size (Calculated Column)
Order Size = IF([Amount] > 5000, "Large", "Small")
// Example 2: Show status indicator (Measure)
Sales Status =
IF(
[Total Sales] >= [Sales Target],
"On Track",
"Below Target"
)// Example 3: Conditional calculation (Measure)
Discount Amount =
IF(
[Total Sales] > 100000,
[Total Sales] * 0.10,
0
)Always include the third argument (value_if_false). Omitting it causes IF to return BLANK() for false conditions. In Power BI visuals, BLANK values are hidden by default — meaning rows that do not meet the condition silently disappear from tables and charts. This is the single most common source of "missing data" bugs in Power BI reports.
Nested IF statements evaluate multiple conditions in sequence by placing one IF inside the false (or true) argument of another IF. Each level adds another decision branch. This is the most common pattern for multi-tier categorization in DAX.
// Nested IF: Customer tier based on annual spending
Customer Tier =
IF(
[Annual Spend] >= 1000000, "Platinum",
IF(
[Annual Spend] >= 500000, "Gold",
IF(
[Annual Spend] >= 100000, "Silver",
"Bronze"
)
)
)// Same logic with SWITCH(TRUE()) — cleaner at 3+ levels
Customer Tier =
SWITCH(
TRUE(),
[Annual Spend] >= 1000000, "Platinum",
[Annual Spend] >= 500000, "Gold",
[Annual Spend] >= 100000, "Silver",
"Bronze"
)EPC Group Rule of Thumb: Use nested IF for 2 conditions. At 3 or more conditions, switch to SWITCH(TRUE()) for readability and maintainability. The DAX engine optimizes both to similar query plans, so the choice is about code quality, not performance.
Nested IF evaluates top-down. Place the most restrictive condition first. If you check [Sales] > 0 before [Sales] > 1000000, the first condition catches everything, and the million-dollar condition never triggers. This is the most common nested IF bug — and it produces no error, just wrong results.
Wrong Order
IF([Sales] > 0, "Has Sales",
IF([Sales] > 1000000, "Enterprise",
"No Sales"
)
)
// "Enterprise" NEVER triggersCorrect Order
IF([Sales] > 1000000, "Enterprise",
IF([Sales] > 0, "Has Sales",
"No Sales"
)
)
// Most restrictive firstCompound conditions combine multiple logical tests into a single IF evaluation. DAX provides both function syntax — AND(), OR() — and operator syntax — &&, || — for combining conditions. The operator syntax is generally preferred for readability.
// IF with AND — both conditions must be true
// Function syntax
Priority Deal =
IF(
AND([Deal Size] > 50000, [Close Probability] > 0.7),
"High Priority",
"Standard"
)
// Operator syntax (preferred — cleaner)
Priority Deal =
IF(
[Deal Size] > 50000 && [Close Probability] > 0.7,
"High Priority",
"Standard"
)// IF with OR — either condition can be true
// Function syntax
Needs Attention =
IF(
OR([Days Overdue] > 30, [Amount] > 100000),
"Escalate",
"Normal Processing"
)
// Operator syntax (preferred)
Needs Attention =
IF(
[Days Overdue] > 30 || [Amount] > 100000,
"Escalate",
"Normal Processing"
)// Combining AND + OR with multiple conditions
Compliance Flag =
IF(
[Industry] = "Healthcare" && ([Amount] > 25000 || [Data Type] = "PHI"),
"HIPAA Review Required",
"Standard Processing"
)Three or more AND conditions? Use variables for clarity. VAR _IsLarge = [Amount] > 50000, VAR _IsUrgent = [Priority] = "High", VAR _IsNew = [Age] < 7, then IF(_IsLarge && _IsUrgent && _IsNew, "Expedite", "Queue"). This makes the logic self-documenting and each condition individually testable.
One of the most frequent questions in Power BI development is when to use IF versus SWITCH. The answer depends on the number of conditions, the type of comparison, and long-term maintainability requirements.
| Scenario | Best Choice | Why |
|---|---|---|
| Binary true/false decision | IF | Clearest syntax for 2 outcomes |
| 3-5 range conditions | SWITCH(TRUE()) | Flat structure avoids nesting |
| Exact value matching (3+) | SWITCH | Evaluates expression once |
| Compound AND/OR logic | IF | Native support for && and || |
| Dynamic measure selection | SWITCH | Clean slicer-to-measure mapping |
| 10+ value pairs | Lookup table | Maintainability — avoid hardcoded DAX |
| Conditional aggregation | IF + CALCULATE | IF controls the calculation flow |
Performance-wise, the DAX engine optimizes IF and SWITCH into similar query plans in most scenarios. The real difference is developer productivity. In EPC Group code reviews across Fortune 500 engagements, nested IF statements beyond 3 levels account for over 40% of DAX bugs — primarily condition ordering errors and mismatched parentheses. SWITCH(TRUE()) eliminates both categories of bugs entirely.
Where you use IF fundamentally changes how it behaves. This distinction trips up intermediate Power BI developers more than any other concept. Understanding the difference is essential for building correct data models.
// Calculated Column — row context
Size Category =
IF(
Orders[Amount] > 5000,
"Large",
"Small"
)// Measure — filter context
Sales Status =
IF(
[Total Sales] >= [Sales Target],
"On Track",
"Below Target"
)Decision Rule: If the IF result is used for slicing, filtering, or building relationships, it must be a calculated column. If the IF result changes based on user interaction (slicer selections, cross-filtering), it must be a measure. When in doubt, prefer a measure — it adds no storage cost and is more flexible.
Combining IF with CALCULATE enables the most powerful pattern in enterprise DAX: conditional aggregation that adapts to context. This pattern drives year-over-year comparisons, conditional KPIs, and dynamic thresholds across every Fortune 500 Power BI deployment EPC Group manages.
// Year-over-Year comparison with IF + CALCULATE
YoY Growth % =
VAR _CurrentSales = [Total Sales]
VAR _PriorYearSales =
CALCULATE(
[Total Sales],
SAMEPERIODLASTYEAR(Dates[Date])
)
RETURN
IF(
_PriorYearSales > 0,
DIVIDE(_CurrentSales - _PriorYearSales, _PriorYearSales),
BLANK()
)// Conditional KPI with traffic-light logic
KPI Status =
VAR _Actual = [Total Sales]
VAR _Target = [Sales Target]
VAR _Achievement = DIVIDE(_Actual, _Target)
RETURN
IF(
_Achievement >= 1.0, "Green",
IF(
_Achievement >= 0.8, "Amber",
"Red"
)
)// Conditional CALCULATE — apply filters only when condition met
Filtered Sales =
IF(
HASONEVALUE(Region[Name]),
CALCULATE(
[Total Sales],
Region[Name] = SELECTEDVALUE(Region[Name])
),
[Total Sales]
)Performance Warning: Always use VAR to pre-compute CALCULATE results before the IF. Without VARs, the DAX engine may evaluate both CALCULATE branches even though only one result is returned. With VARs, each CALCULATE runs exactly once and the result is cached for the IF comparison.
Robust error handling prevents broken visuals and unexpected BLANK values in production reports. DAX provides several functions that work alongside IF to create defensive formulas that handle bad data gracefully.
IFERROR evaluates an expression and returns an alternate value if the expression produces an error. It catches division by zero, type mismatches, and invalid function arguments.
// Safe division — returns 0 instead of error Profit Margin = IFERROR(DIVIDE([Profit], [Revenue]), 0) // Better: Use DIVIDE's built-in alternate result Profit Margin = DIVIDE([Profit], [Revenue], 0)
ISBLANK returns TRUE when a value is BLANK (null). Use it inside IF to provide default values or conditional logic for missing data.
// Replace blank with default value
Display Name =
IF(
ISBLANK([Preferred Name]),
[Legal Name],
[Preferred Name]
)
// Cleaner alternative with COALESCE (DAX 2020+)
Display Name = COALESCE([Preferred Name], [Legal Name], "Unknown")For maximum robustness in enterprise reports, combine multiple defensive checks. This pattern handles both blank inputs and calculation errors.
Safe Growth % =
VAR _Current = [Total Sales]
VAR _Prior = CALCULATE([Total Sales], SAMEPERIODLASTYEAR(Dates[Date]))
RETURN
IF(
ISBLANK(_Prior) || _Prior = 0,
BLANK(),
IFERROR(
DIVIDE(_Current - _Prior, _Prior),
BLANK()
)
)Anti-pattern: Do not wrap entire measures in IFERROR as a "safety net." This masks real bugs. If a division fails, fix the denominator check explicitly with ISBLANK or DIVIDE. If a RELATED lookup fails, fix the relationship. IFERROR should be a targeted tool, not a blanket suppressor.
IF itself is one of the cheapest operations in DAX. The performance cost comes from what you put inside the IF. These optimization patterns are drawn from EPC Group's experience tuning enterprise Power BI models with 50M+ row tables.
Without VAR, expensive expressions inside IF may be evaluated multiple times — once for the condition and once for the result.
// BAD: CALCULATE runs twice IF(CALCULATE([Sales]) > 0, CALCULATE([Sales]) / [Target], 0) // GOOD: CALCULATE runs once VAR _Sales = CALCULATE([Sales]) RETURN IF(_Sales > 0, _Sales / [Target], 0)
SUMX, AVERAGEX, and other iterators execute their expression once per row. IF inside these functions runs millions of times on large tables.
// SLOW: IF runs once per row (10M evaluations) SUMX(Sales, IF([Category] = "Premium", [Amount] * 1.1, [Amount])) // FAST: Filter then aggregate (2 CALCULATE calls) VAR _Premium = CALCULATE([Total Amount], Products[Category] = "Premium") * 1.1 VAR _Standard = CALCULATE([Total Amount], Products[Category] <> "Premium") RETURN _Premium + _Standard
If the IF result never changes with filter context (e.g., static categorization based on a column value), compute it in Power Query (M) instead of DAX. Power Query runs during refresh, not at query time.
// In Power Query (M) — computed during refresh
= Table.AddColumn(Source, "Size", each
if [Amount] > 5000 then "Large" else "Small"
)
// Result: A pre-computed column with zero query-time costThe DAX engine is lazy — it only evaluates a VAR when it is referenced. Store both IF branches in VARs to keep the code clean without performance penalty.
// Both VARs defined but only one is evaluated
VAR _DetailView =
CONCATENATEX(Products, [ProductName], ", ")
VAR _SummaryView =
COUNTROWS(Products) & " products"
RETURN
IF(
COUNTROWS(Products) > 10,
_SummaryView,
_DetailView
)These are the IF-related errors EPC Group encounters most frequently during Power BI code reviews. Each mistake produces incorrect results silently — the report renders without errors but shows wrong numbers.
When the false argument is omitted, IF returns BLANK(). In table visuals, BLANK rows are hidden by default — data silently disappears.
Wrong
Sales Label = IF([Sales] > 10000, "High") // Sales <= 10000 → BLANK → row hidden in table
Correct
Sales Label = IF([Sales] > 10000, "High", "Standard") // Every row gets a visible label
Less restrictive conditions placed before more restrictive ones catch all values, making deeper conditions unreachable.
Wrong
IF([Score] > 50, "Pass", IF([Score] > 90, "Excellent", "Fail")) // Score=95 → "Pass" (never reaches "Excellent")
Correct
IF([Score] > 90, "Excellent", IF([Score] > 50, "Pass", "Fail")) // Most restrictive first
DAX string comparison is case-insensitive by default, but leading/trailing spaces cause mismatches that are invisible in the data view.
Wrong
IF([Status] = "Active", "Yes", "No") // " Active" or "Active " won't match
Correct
IF(TRIM([Status]) = "Active", "Yes", "No") // TRIM removes leading/trailing spaces
IF cannot change filter context. If you need conditional filtering, you need CALCULATE — not IF wrapping a SUM.
Wrong
East Sales = IF([Region] = "East", SUM([Amount]), 0) // In a measure, [Region] is not row-level
Correct
East Sales = CALCULATE(SUM([Amount]), Region[Name] = "East") // CALCULATE modifies filter context correctly
Returning a number from one branch and text from another causes type conversion issues and unexpected visual behavior.
Wrong
Result = IF([Sales] > 0, [Sales], "No Sales") // Returns text column — loses numeric formatting
Correct
Result = IF([Sales] > 0, [Sales], 0) Label = IF([Sales] > 0, FORMAT([Sales], "$#,##0"), "No Sales")
The IF function in Power BI DAX uses the syntax IF(logical_test, value_if_true, value_if_false). For example, IF([Sales] > 10000, "High", "Low") returns "High" when sales exceed 10,000 and "Low" otherwise. The third argument (value_if_false) is optional — if omitted, it returns BLANK() when the condition is false. IF works in both calculated columns (evaluated per row) and measures (evaluated in filter context). Always include the false argument explicitly to avoid unexpected BLANK values in your reports.
Nested IF statements in Power BI place one IF inside another to evaluate multiple conditions. The syntax is IF(condition1, result1, IF(condition2, result2, IF(condition3, result3, else_result))). For example: IF([Score] >= 90, "A", IF([Score] >= 80, "B", IF([Score] >= 70, "C", "F"))). Nest up to 2-3 levels maximum. Beyond that, switch to SWITCH(TRUE(), condition1, result1, condition2, result2, else_result) for readability. Each nesting level adds parentheses that become increasingly difficult to debug.
IF handles binary true/false conditions and supports range comparisons natively: IF([Sales] > 1000, "High", "Low"). SWITCH maps an expression against multiple exact values: SWITCH([Region], "East", 1, "West", 2, 3). For 2 conditions, use IF. For 3+ conditions, use SWITCH for cleaner code. SWITCH(TRUE()) handles range conditions like nested IF but with flat, readable syntax. Performance is nearly identical — the DAX engine optimizes both into similar query plans. The real difference is maintainability: SWITCH scales better as conditions grow.
Combine IF with AND() or OR() for compound conditions. AND syntax: IF(AND([Sales] > 1000, [Region] = "East"), "Target Met", "Below Target"). OR syntax: IF(OR([Status] = "Active", [Status] = "Pending"), "In Progress", "Closed"). You can also use the && operator for AND and || for OR: IF([Sales] > 1000 && [Region] = "East", "Target Met", "Below Target"). For 3+ conditions, nest AND/OR: IF(AND([Sales] > 1000, AND([Region] = "East", [Year] = 2026)), "Match", "No Match"), or use && for cleaner syntax: IF([Sales] > 1000 && [Region] = "East" && [Year] = 2026, "Match", "No Match").
Yes, IF works in both calculated columns and measures, but they behave differently. In a calculated column, IF evaluates once per row during data refresh and the result is stored in the model: OrderSize = IF([Amount] > 5000, "Large", "Small"). In a measure, IF evaluates dynamically based on the current filter context: Sales Category = IF([Total Sales] > 100000, "High Volume", "Standard"). Calculated columns increase model size but are faster at query time. Measures are computed on demand but add no storage. Use calculated columns for static categorization and measures for context-dependent logic.
IFERROR wraps an expression and returns an alternative value if the expression produces an error. Syntax: IFERROR(expression, value_if_error). Example: IFERROR(DIVIDE([Revenue], [Costs]), 0) returns 0 instead of an error when Costs is zero. IFERROR catches all error types: division by zero, type mismatches, and invalid arguments. However, avoid using IFERROR as a blanket wrapper — it masks bugs. Instead, handle specific cases: use DIVIDE() with its built-in alternate result for division, ISBLANK() to test for empty values, and ISERROR() inside IF when you need different handling for different error types.
Use ISBLANK() or the strict equality check with BLANK() inside IF. Syntax: IF(ISBLANK([Field]), "No Value", [Field]). Alternative: IF([Field] = BLANK(), "No Value", [Field]). Important distinction: ISBLANK returns TRUE only for BLANK, while the equality check ([Field] = BLANK()) also returns TRUE for empty strings in some contexts. For robust null handling, use: IF(ISBLANK([Field]) || [Field] = "", "No Value", [Field]). In measures, remember that BLANK propagates through calculations — BLANK + 100 = 100, not BLANK. Use COALESCE([Field], 0) as a cleaner alternative to IF(ISBLANK([Field]), 0, [Field]).
IF itself is lightweight — the performance impact comes from what is inside the IF. Three rules: 1) Store expensive expressions in VAR before the IF to prevent re-evaluation: VAR _Total = CALCULATE([Sales]) RETURN IF(_Total > 0, _Total / [Target], 0). 2) Avoid IF inside row-level iterators (SUMX, AVERAGEX) over large tables — pre-calculate categories in Power Query instead. 3) IF with CALCULATE inside both branches forces the engine to evaluate both filter contexts even though only one result is returned. Use VAR to pre-compute both branches. In most reports, IF adds negligible overhead. Focus optimization on the expressions within IF, not IF itself.
IF with CALCULATE enables conditional aggregation based on dynamic criteria. Example: Conditional Sales = IF(HASONEVALUE(Region[Name]), CALCULATE([Total Sales], Region[Name] = SELECTEDVALUE(Region[Name])), [Total Sales]). Common pattern: IF(ISFILTERED(Date[Year]), CALCULATE([Sales], SAMEPERIODLASTYEAR(Date[Date])), BLANK()). Important: place CALCULATE results in VARs to avoid double evaluation: VAR _Current = CALCULATE([Sales]) VAR _Prior = CALCULATE([Sales], SAMEPERIODLASTYEAR(Date[Date])) RETURN IF(_Prior > 0, (_Current - _Prior) / _Prior, BLANK()). This pattern is essential for year-over-year comparison measures in enterprise dashboards.
EPC Group has built and optimized DAX formulas for Fortune 500 organizations for over 25 years. From IF statement best practices to full-scale Power BI performance optimization, our team delivers measurable results across healthcare, finance, and government.