Business Professionals
Power BI | Power Pivot | Power Query | DAX
Cloud Flows | RPA | AI Builder | Copilot
60+ Formulas | Data Stories | Advanced Reporting & Modeling
VB Programming | Report Automation |
MS-Office Automation
Techno-Business Professionals
Power BI | Power Query | Advanced DAX | SQL - Query &
Programming
Microsoft Fabric | Power BI | Power Query | Advanced DAX |
SQL - Query & Programming
Power BI | Power Apps | Power Automate | Copilot Studio | Power Pages | Dataverse
Microsoft Power Apps | Microsoft Power Automate
Power BI | Adv. DAX | SQL (Query & Programming) |
VBA | Python | Web Scrapping | API Integration
Power BI | Power Apps | Power Automate |
SQL (Query & Programming)
Power BI | Adv. DAX | Power Apps | Power Automate |
SQL (Query & Programming) | VBA | Python | Web Scrapping | API Integration
Power Apps | Power Automate | SQL | VBA | Python |
Web Scraping | RPA | API Integration
Technology Professionals
Power BI | DAX | SQL | ETL with SSIS | SSAS | VBA | Python
Power BI | SQL | Azure Data Lake | Synapse Analytics |
Data Factory | Databricks | Power Apps | Power Automate |
Azure Analysis Services
Microsoft Fabric | Power BI | SQL | Lakehouse |
Data Factory (Pipelines) | Dataflows Gen2 | KQL | Delta Tables | Power Apps | Power Automate
Power BI | Power Apps | Power Automate | SQL | VBA | Python | API Integration
Power BI | Advanced DAX | Databricks | SQL | Lakehouse Architecture
Business Professionals
Power BI | Power Pivot | Power Query | DAX
Cloud Flows | RPA | AI Builder | Copilot
60+ Formulas | Data Stories | Advanced Reporting & Modeling
VB Programming | Report Automation |
MS-Office Automation
Techno-Business Professionals
Power BI | Power Query | Advanced DAX | SQL - Query &
Programming
Microsoft Fabric | Power BI | Power Query | Advanced DAX |
SQL - Query & Programming
Power BI | Power Apps | Power Automate | Copilot Studio | Power Pages | Dataverse
Microsoft Power Apps | Microsoft Power Automate
Power BI | Adv. DAX | SQL (Query & Programming) |
VBA | Web Scrapping | API Integration
Power BI | Power Apps | Power Automate |
SQL (Query & Programming)
Power BI | Adv. DAX | Power Apps | Power Automate |
SQL (Query & Programming) | VBA | Web Scrapping | API Integration
Power Apps | Power Automate | SQL | VBA |
Web Scraping | RPA | API Integration
Technology Professionals
Power BI | DAX | SQL | ETL with SSIS | SSAS | VBA
Power BI | SQL | Azure Data Lake | Synapse Analytics |
Data Factory | Azure Analysis Services
Microsoft Fabric | Power BI | SQL | Lakehouse |
Data Factory (Pipelines) | Dataflows Gen2 | KQL | Delta Tables
Power BI | Power Apps | Power Automate | SQL | VBA | API Integration
Power BI | Advanced DAX | Databricks | SQL | Lakehouse Architecture
A lot of Power BI models start fast and die messy: dozens of measures, conflicting business definitions, and a semantic model nobody wants to touch. The recurring question behind that pain is simple: should this business logic live in advanced DAX or in MS-SQL? This article walks through a realistic scenario and gives you a practical way to decide where each piece of logic belongs so your models stay fast and maintainable.
You inherit a Sales Analytics dataset:
FactSales, FactBudgetDimDate, DimCustomer, DimProduct, DimSalesOrgThe team has grown the model for a couple of years:
Sales, Sales_Adj, Sales_Final)Symptoms:
We’ll use this scenario as the anchor while we walk through where logic belongs.
Before arguing DAX vs MS-SQL, classify the logic. For this scenario, the key categories:
Data quality and conformance
Structural logic
Business rules and metrics
User-facing calculation logic
You need different tools for each. Trying to do all of them in DAX or all of them in MS-SQL is what breaks maintainability.
From the SQL Server side, you typically have:
For an Import mode Power BI model, the most relevant are views and pre-shaped tables.
Data quality and conformance
Example: standardising customer status codes.
CREATE VIEW dbo.vDimCustomer AS
SELECT
c.CustomerKey,
c.CustomerCode,
c.CustomerName,
CASE
WHEN c.StatusCode IN ('A', 'ACTIVE', '1') THEN 'Active'
WHEN c.StatusCode IN ('I', 'INACTIVE', '0') THEN 'Inactive'
ELSE 'Unknown'
END AS CustomerStatus,
c.RegionCode,
c.CreatedDate
FROM dbo.DimCustomer c;
This view gives Power BI a clean, canonical CustomerStatus instead of pushing that mapping into DAX measures.
Structural logic and relationships
Example: SCD Type 2 customer dimension exposed via a view that selects the current version.
CREATE VIEW dbo.vDimCustomerCurrent AS
SELECT
c.CustomerKey,
c.CustomerCode,
c.CustomerName,
c.CustomerStatus,
c.RegionCode
FROM dbo.DimCustomer c
WHERE c.IsCurrent = 1;
Heavy joins and pre-aggregation for stable metrics
Example: pre-aggregated net sales by customer and month.
CREATE VIEW dbo.vFactSalesMonthly AS
SELECT
s.CustomerKey,
s.DateKey,
SUM(s.GrossAmount - s.DiscountAmount) AS NetSalesAmount
FROM dbo.FactSales s
GROUP BY
s.CustomerKey,
s.DateKey;
This view becomes a fact table in Power BI for many metrics; DAX then builds ratios and time intelligence on top.
In our Sales Analytics scenario, moving status standardisation, SCD handling and base net sales aggregation into views immediately reduces measure complexity and eliminates several conflicting measures.
In the Power BI semantic model, DAX operates over:
User-facing metrics and KPIs
Example: Net Sales measure using the pre-aggregated view.
Net Sales :=
SUM ( vFactSalesMonthly[NetSalesAmount] )
If you don’t have pre-aggregation in SQL, you can still do it in DAX:
Net Sales (from FactSales) :=
SUMX (
FactSales,
FactSales[GrossAmount] - FactSales[DiscountAmount]
)
Time intelligence
Example: YoY Net Sales using a proper marked date table.
Net Sales YoY :=
VAR CurrentPeriod = [Net Sales]
VAR PreviousPeriod =
CALCULATE (
[Net Sales],
DATEADD ( DimDate[Date], -1, YEAR )
)
RETURN
DIVIDE ( CurrentPeriod - PreviousPeriod, PreviousPeriod )
Dynamic business rules based on filter context
Example: active customer in the last 90 days based on user filter context.
Active Customers (90d) :=
VAR Last90Days =
DATESINPERIOD ( DimDate[Date], MAX ( DimDate[Date] ), -90, DAY )
RETURN
DISTINCTCOUNT (
CALCULATETABLE (
FactSales[CustomerKey],
Last90Days
)
)
This kind of logic is hard to express once-and-for-all in SQL, because it depends on the current filter context. DAX is built for it.
In our Sales Analytics scenario, moving dynamic “active customer” definitions and time intelligence into DAX while keeping structural status logic in SQL makes definitions clearer and easier to maintain.
Instead of rules-of-thumb like “do everything in DAX” or “push everything down”, use a simple decision framework.
For each piece of logic, ask:
Is the logic tied to user selections or report context?
Does the logic define core data structure or quality?
Is the logic reused across multiple tools, not just Power BI?
Is the logic simple but context-dependent (filters, time)?
Is performance currently a problem, and is this logic heavy on joins or row operations?
Let’s apply this to three painful measures from our scenario.
Existing measure:
Net Sales Active Customers :=
CALCULATE (
[Net Sales],
KEEPFILTERS (
FILTER (
DimCustomer,
DimCustomer[StatusCode] IN { "A", "ACTIVE", "1" }
)
)
)
Problems:
DimCustomer don’t see the mapping.Refactor:
vDimCustomer in SQL (as shown earlier).Net Sales Active Customers :=
CALCULATE ( [Net Sales], DimCustomer[CustomerStatus] = "Active" )
Existing SQL view:
CREATE VIEW dbo.vActiveCustomer90d AS
SELECT DISTINCT s.CustomerKey
FROM dbo.FactSales s
WHERE s.InvoiceDate >= DATEADD(DAY, -90, CAST(GETDATE() AS date));
Used in Power BI to filter DimCustomer via a relationship.
Problems:
Refactor:
Active Customers (90d) measure).Existing DAX calculated table:
Sales with Budget :=
SUMMARIZECOLUMNS (
DimCustomer[CustomerKey],
DimProduct[ProductKey],
DimDate[Date],
"Sales", [Net Sales],
"Budget", [Budget Amount]
)
Problems:
Refactor:
vFactSalesBudget view in SQL joining FactSales and FactBudget with the agreed business rules.The storage mode affects where logic can realistically live.
Practical guidance:
Practical guidance:
In our Sales Analytics scenario, if the dataset moves to DirectQuery for near-real-time data, more of the heavy joining and pre-aggregation should move to MS-SQL views to avoid slow visuals.
The biggest maintainability gains come from making logic discoverable.
Practical steps for the scenario team:
Define a single source for each core concept
Document where each rule lives
Use naming conventions
vDim* for dimensions, vFact* for facts, vKpi* for complex pre-aggregated structures._Base for base measures and avoid creating multiple near-duplicates.Align SQL and Power BI teams
After applying this in the Sales Analytics model, the team ends up with:
When you add or change a business rule in Power BI, decide explicitly: is this a structural, shared definition (move to MS-SQL), or an interactive, user-context-dependent metric (keep in DAX)? Writing that decision down for each major rule will do more for your model’s long-term health than any single optimisation trick.
This article reflects a shift toward deliberately splitting business logic between MS-SQL and DAX, especially in teams running shared Power BI datasets over SQL-based warehouses that have grown organically and become hard to maintain.
Professionals who want to apply these patterns to their own data can explore Excelgoodies' Power BI With SQL programme - taught live by instructors, with certification awarded once a real project is running at work.
Insights compiled through ongoing industry research and discussions within the Excelgoodies Analytics Community.
MS-SQL
New
Next Batches Now Live
Power BI
SQL
Power Apps
Power Automate
Microsoft Fabrics
Azure Data Engineering