Power BI DAX Tutorial

Build a Complete Power BI Sales Dashboard — Step-by-Step 2025 Guide

Screenshot: Finished Sales Dashboard
The finished Power BI Sales Dashboard — built step by step in this guide.

A Power BI Sales Dashboard is the single most requested BI project I get — and for good reason. It gives sales managers, CFOs, and operations teams a real-time view of revenue, margins, and team performance without waiting for a manual Excel update.

In this guide, I'll walk you through building a production-ready sales dashboard from scratch — the same approach I use on client projects. By the end, you'll have a complete dashboard connected to SQL Server, with calculated DAX measures, drill-through capabilities, and published to Power BI Service.

Prerequisites

You'll need Power BI Desktop (free download from Microsoft), a basic understanding of Excel, and optionally SQL Server or a sample dataset. I'll provide sample data links in each section.

1. Planning Your Dashboard

Before opening Power BI, spend 15 minutes answering these questions. They'll save you hours of rework later:

  • Who is the primary user? Sales Director, Regional Manager, or Sales Rep — each needs different detail levels.
  • What decisions does this dashboard need to support? Revenue targets, rep performance, product mix — be specific.
  • What are the top 5 KPIs? Total Revenue, Gross Margin %, Top Products, Sales by Region, MTD vs Target.
  • What filters will users need? Date range, region, product category, sales rep.
  • What data sources are involved? ERP, CRM, Excel file, SQL database?

For this tutorial, we're building for a Sales Director who needs to track revenue, margins, and rep performance across 4 regions. Our KPIs are: Total Revenue, Gross Margin %, Sales vs Target, Top 10 Products, and MTD Revenue.

2. Connecting Your Data Sources

Go to Home → Get Data → SQL Server. Enter your server name and credentials. For this tutorial, we'll use three tables from a Sales database:

  • dbo.FactSales — transaction-level sales records
  • dbo.DimProduct — product master with category, cost, and list price
  • dbo.DimDate — a proper date dimension table
Pro Tip: Always Use a Date Dimension

Never use auto date/time in Power BI for production dashboards. A proper DimDate table gives you full control over time intelligence and dramatically improves DAX performance.

Creating the Date Dimension in SQL

If you don't have a date dimension, here's the SQL to generate one:

SQL
-- Create a complete Date Dimension table
CREATE TABLE dbo.DimDate (
    DateKey       INT         PRIMARY KEY,
    FullDate      DATE        NOT NULL,
    Year          INT         NOT NULL,
    Quarter       INT         NOT NULL,
    Month         INT         NOT NULL,
    MonthName     VARCHAR(20)  NOT NULL,
    WeekNumber    INT         NOT NULL,
    DayOfWeek     INT         NOT NULL,
    IsWeekend     BIT         NOT NULL,
    FiscalYear    INT         NOT NULL,
    FiscalQuarter INT         NOT NULL
);

-- Populate with dates from 2020 to 2030
DECLARE @StartDate DATE = '2020-01-01';
DECLARE @EndDate   DATE = '2030-12-31';
DECLARE @Date      DATE = @StartDate;

WHILE @Date <= @EndDate
BEGIN
    INSERT INTO dbo.DimDate VALUES (
        CONVERT(INT, FORMAT(@Date, 'yyyyMMdd')),
        @Date,
        YEAR(@Date),
        DATEPART(QUARTER, @Date),
        MONTH(@Date),
        DATENAME(MONTH, @Date),
        DATEPART(WEEK, @Date),
        DATEPART(WEEKDAY, @Date),
        CASE WHEN DATEPART(WEEKDAY, @Date) IN (1,7) THEN 1 ELSE 0 END,
        CASE WHEN MONTH(@Date) >= 4 THEN YEAR(@Date) + 1 ELSE YEAR(@Date) END,
        CASE WHEN MONTH(@Date) IN (4,5,6) THEN 1
             WHEN MONTH(@Date) IN (7,8,9) THEN 2
             WHEN MONTH(@Date) IN (10,11,12) THEN 3
             ELSE 4 END
    );
    SET @Date = DATEADD(DAY, 1, @Date);
END

3. Building the Data Model

After loading your tables, go to Model View. You should create a Star Schema — one fact table in the centre, dimension tables around it. This is the foundation of every well-performing Power BI report.

  • Connect FactSales.DateKeyDimDate.DateKey (Many-to-One)
  • Connect FactSales.ProductKeyDimProduct.ProductKey (Many-to-One)
  • Connect FactSales.CustomerKeyDimCustomer.CustomerKey (Many-to-One)
Avoid Many-to-Many Relationships

If Power BI suggests a many-to-many relationship, it's a red flag — usually means your data model has duplicates or you need a bridge table. Fix the data, don't accept the warning.

Screenshot: Star Schema in Power BI Model View
Star schema with FactSales at the centre and 3 dimension tables.

4. Writing the Core DAX Measures

All measures go into a dedicated Measures Table (right-click → Enter Data → create an empty table called "_Measures"). This keeps your model clean.

Revenue Measures

DAX
// ── Core Revenue Measures ──────────────────────────

Total Revenue =
SUMX(
    FactSales,
    FactSales[Quantity] * FactSales[UnitPrice]
)

Total COGS =
SUMX(
    FactSales,
    FactSales[Quantity] * RELATED(DimProduct[UnitCost])
)

Gross Profit =
[Total Revenue] - [Total COGS]

Gross Margin % =
DIVIDE(
    [Gross Profit],
    [Total Revenue],
    0  -- return 0 if Revenue is blank (avoid divide-by-zero)
)

Time Intelligence Measures

Time intelligence is where Power BI's DAX really shines. These measures use your DimDate table, so mark it as a Date Table first (right-click → Mark as date table).

DAX
// ── Time Intelligence ──────────────────────────────

Revenue MTD =
TOTALMTD([Total Revenue], DimDate[FullDate])

Revenue YTD =
TOTALYTD([Total Revenue], DimDate[FullDate])

Revenue LY =
CALCULATE(
    [Total Revenue],
    SAMEPERIODLASTYEAR(DimDate[FullDate])
)

Revenue YoY % =
DIVIDE(
    [Total Revenue] - [Revenue LY],
    [Revenue LY],
    BLANK()
)

Revenue vs Target =
[Total Revenue] - [Sales Target]

Revenue vs Target % =
DIVIDE([Revenue vs Target], [Sales Target], 0)
DAX Best Practice: Always Use DIVIDE()

Never use the / operator for division in DAX. DIVIDE(numerator, denominator, alternateResult) handles blank and zero denominators gracefully — preventing errors in your dashboard.

Ranking Measures

DAX
// ── Rankings ───────────────────────────────────────

Product Rank by Revenue =
RANKX(
    ALLSELECTED(DimProduct[ProductName]),
    [Total Revenue],
    ,
    DESC,
    DENSE
)

Top N Products =
VAR TopN = SELECTEDVALUE(TopNSelector[N], 10)
RETURN
IF(
    [Product Rank by Revenue] <= TopN,
    [Total Revenue],
    BLANK()
)

5. Designing the Visuals

Now the creative part. A great dashboard follows a clear reading hierarchy:

ZoneContentVisuals
Top stripKPI CardsCard, KPI visual
Left mainRevenue trend over timeLine/Area chart
Right mainRevenue by Region / RepBar chart, Map
BottomTop Products, Detail tableTable, Matrix
Right sidebarFiltersSlicers

KPI Card Setup

For each KPI Card, use the New Card visual (not the legacy Card). Add your measure to the "Values" field, then configure:

  • Callout value: Your main measure (e.g., [Total Revenue])
  • Reference label: [Revenue YoY %] with conditional formatting (green ↑ / red ↓)
  • Category label: Plain text description
Conditional Formatting Tip

In card visuals, set the reference value font colour using a DAX measure: KPI Colour = IF([Revenue YoY %] >= 0, "#10B981", "#EF4444") — then apply it as a dynamic colour in Format → Callout Value → Font color → Field value.

6. Adding Row-Level Security (RLS)

If different sales reps should only see their own data, set up RLS. In Modeling → Manage Roles:

DAX — RLS Filter
// Applied to DimSalesRep table
// USERNAME() returns the logged-in user's email in Power BI Service

[Email] = USERNAME()

Then publish the report and assign users to RLS roles in Power BI Service → Dataset Settings → Security.

7. Publishing to Power BI Service

Once your report is tested and validated:

  1. Go to Home → Publish — sign in with your Power BI account
  2. Select your target workspace (create one for the client)
  3. In Power BI Service, configure the Data Gateway if using SQL Server
  4. Set up a Scheduled Refresh — daily at 6 AM usually works well
  5. Create a Dashboard by pinning key visuals from the report
  6. Share with users or configure an App for broader distribution
Gateway Required for On-Premise Data

If your SQL Server is on-premises (not Azure), you must install the On-premises Data Gateway on a server that's always on. Without it, scheduled refresh won't work.

8. Performance Optimisation Checklist

Before handing over to the client, run through this checklist:

  • Star schema — no many-to-many, no bidirectional filters (unless necessary)
  • Import mode — not DirectQuery unless you genuinely need real-time data
  • Remove unused columns — every column loads into memory
  • Use aggregations for very large tables (10M+ rows)
  • Avoid calculated columns for things you can do in Power Query
  • Use variables in DAX (VAR/RETURN) to avoid re-evaluation
  • Test with Performance Analyzer (View → Performance Analyzer)

Ankit Kumar

Data Analyst · Power BI Developer · ETLGuru.in

Ankit is a Data Analyst and founder of Pyivot Solutions with 5+ years building production-grade Power BI dashboards, SQL solutions, and analytics systems for manufacturing, trading, and e-commerce businesses across India.