A Power BI dashboard that turns raw retail transactions into a clear view of top-performing countries, months, brands, and customer segments.

Transaction logs existed, but there was no fast way to see which brand, month, or region was actually driving sales without manually pivoting spreadsheets.
An interactive dashboard that lets a non-technical stakeholder drill from company-wide revenue down to a single brand or customer segment in a few clicks.
This is the classic "which country, which month, which product" question every retail business asks — and it's a great first BI project because the answer isn't in one table, it's in how you rank and segment across several dimensions at once. You can build this exact project with the free, public UCI Online Retail dataset (a real 500,000+ row transaction export from a UK-based online retailer), so every query below is copy-paste runnable.
One flat transaction table is all you need to start:
| Column | Example | Notes |
|---|---|---|
invoice_no | 536365 | Invoices starting with "C" are cancellations |
stock_code | 85123A | Product identifier |
description | WHITE HANGING HEART T-LIGHT HOLDER | Product name |
quantity | 6 | Negative values are returns |
invoice_date | 2010-12-01 08:26:00 | |
unit_price | 2.55 | |
customer_id | 17850 | ~25% of rows have no customer ID |
country | United Kingdom |
Retail exports are messy by default — cancelled invoices, returns, and missing customer IDs will all silently wreck your totals if you don't filter them first:
create table stg_retail_sales as
select
invoice_no,
stock_code,
description,
quantity,
invoice_date,
unit_price,
customer_id,
country,
quantity * unit_price as line_revenue
from raw_online_retail
where invoice_no not like 'C%' -- exclude cancellations
and quantity > 0 -- exclude returns
and unit_price > 0
and customer_id is not null;Sanity-check it immediately:
select country, sum(line_revenue) as revenue
from stg_retail_sales
group by country
order by revenue desc
limit 10;If the UK doesn't dominate this result for the UCI dataset, something upstream in your cleaning step is wrong — that's a useful self-check before you ever open Power BI.
The naive approach is sorting a table visual by a column — it works until someone filters by country or date, and the ranking silently stops updating. Use RANKX instead, so the rank recalculates inside whatever filter context the report is in:
Total Revenue = SUM(Sales[LineRevenue])
Product Rank =
RANKX(
ALL(Sales[Description]),
[Total Revenue],
,
DESC
)
Best Selling Month =
CALCULATE(
SELECTEDVALUE('Date'[Month Name]),
TOPN(1, ALL('Date'[Month Name]), [Total Revenue], DESC)
)A simple order-count bucket is enough to make the "which customers matter most" question visual without building a full RFM model:
Customer Orders = DISTINCTCOUNT(Sales[InvoiceNo])
Customer Segment =
SWITCH(
TRUE(),
[Customer Orders] >= 10, "VIP",
[Customer Orders] >= 3, "Repeat",
"New"
)
Segment Contribution % =
DIVIDE(
[Total Revenue],
CALCULATE([Total Revenue], ALL(Customers[Customer Segment]))
)Three pages cover the whole story without overwhelming a first-time viewer:
Product Rank, with a slicer for country and month.The detail worth mentioning unprompted: why RANKX over a plain sort. A sorted column looks identical to a dynamic rank until someone applies a filter — showing you understand that distinction is a stronger signal than the dashboard itself.