,

Power BI starter: model before visuals

11 min read
Editorial featured image for Power BI starter: model before visuals. Title text reads Power BI starter: model before visuals.

You open Power BI Desktop, connect to a spreadsheet, and drop three charts on a page before lunch. The bars look fine. The meeting starts. Someone filters to one region and revenue doubles. Someone else asks why “total customers” does not match the CRM. The room goes quiet in that special way that means the dashboard is now the problem, not the data. That moment is almost never a formatting issue. It is a model issue that you painted over with visuals.

This is Part 1 of Power BI starter. We put the star schema, relationships, and grain before pretty pages. Parts 2 and 3 cover measures that match the question, then publish, share, and trust. You do not need to become a full-time data modeler. You need a habit that keeps your first real report honest. For the wider AMS map, start at Learn. For metric definitions that travel with you, see Metrics that matter. For the warehouse SQL habits that feed BI tools, keep the SQL series nearby.

What you’ll learn

  • Why “model before visuals” saves more time than another chart type
  • How fact tables and dimension tables split work in a star schema
  • How to state grain in one sentence before you create relationships
  • Which relationship settings break filters and invent double counts
  • A worked sales starter model with tables, keys, and a sanity check
  • Common Power BI modeling mistakes and a practice path for this week

Why visuals first feels fast and fails later

Power BI makes it easy to drag fields onto a canvas. That speed is real. The trap is treating the report page as the place where logic lives. When totals, filters, and drill-through disagree, people argue about chart titles instead of asking what one row means. Visuals are views on a model. If the model is a pile of flat exports glued with many-to-many guesses, every new page inherits the mess.

Think of a spreadsheet with order lines, customer names, product names, and region names all in one giant table. It works for a single pivot. It fails when you need “orders per customer this month” and “units per product this month” on the same page with clean filters. You start inventing calculated columns that paper over duplicates. Then someone refreshes a new extract and the paper tears.

A model-first habit is simple: before you design the first bar chart, write down the business questions, the grain of each table, and which keys connect them. Then build the smallest star that answers those questions. Then, and only then, pick visuals that show the answers without hiding the structure.

Rule of thumb: If you cannot say “one row in this table means…” in a full sentence, stop drawing. Fix grain first.

Star schema in plain English

A star schema is a layout with one or more fact tables in the middle and dimension tables around them. Facts hold events or measurements you count or sum: order lines, page views, support tickets, invoice amounts. Dimensions hold the nouns you filter and group by: date, customer, product, store, campaign.

Facts tend to be long and narrow: lots of rows, keys, and a few measures or measure-friendly columns. Dimensions tend to be shorter (relative to facts) and wider with attributes people actually read: product category, customer segment, store region. The “star” name comes from a diagram with the fact in the center and dimensions as points. You do not need perfect warehouse purity. You need separation that matches how people ask questions.

Facts answer “how much / how many / when did it happen?”

Example fact: fact_order_lines. One row might mean one product line on one order. Columns might include order_id, order_line_id, order_date_key, customer_key, product_key, quantity, net_amount. You sum net_amount. You count distinct order_id when “orders” is the real question, not “lines.”

Dimensions answer “which slice?”

Example dimensions: dim_date, dim_customer, dim_product. Each has a key that matches the fact and attributes people use in slicers. Put the pretty names in dimensions, not duplicated in every fact row, when you can. That keeps renames and hierarchies in one place.

Why not one flat table forever?

Flat tables are fine for a throwaway exploration. They become expensive when attributes change history, when you need role-playing dates (order date vs ship date), or when multiple facts share the same dimensions. Stars scale because new facts can reuse dim_date and dim_customer without rewriting every page.

Grain is the contract for every table

Grain is the meaning of one row. If two people disagree on grain, they will disagree on every total that comes from that table. Write grain as a sentence, not a feeling.

TableGrain sentenceGood key idea
fact_order_linesOne row = one product line on one orderorder_line_id unique
fact_ordersOne row = one order headerorder_id unique
dim_customerOne row = one customer as currently definedcustomer_key unique
dim_dateOne row = one calendar daydate_key unique

Mixing order-line and order-header grain in one table is a classic double-count machine. Summing “shipping fee” that exists only on the header while the table is at line grain multiplies shipping across lines. Power BI will happily draw the wrong number with a beautiful label.

If you need both line metrics and header metrics, prefer two facts that share dimensions, or a careful measure design in Part 2. Do not “just average it” in a visual and hope nobody notices.

Relationships: the filters you do not see

In Power BI, relationships tell the engine how filters flow. A slicer on dim_product[Category] should filter fact_order_lines through the product key. That only works if the relationship exists, uses the right columns, and has a sensible cardinality and filter direction.

Cardinality in human words

  • One to many (dimension to fact): the default you want most of the time. One customer has many order lines.
  • One to one: rare for starters. Sometimes a bridge or a carefully designed extension table.
  • Many to many: a warning light for beginners. Sometimes valid with a bridge table. Often a sign that grain is wrong or keys are dirty.

Filter direction

Single direction from dimension to fact is the usual starter choice. Bidirectional filters can solve a specific multi-fact problem and also create surprise loops where a filter on one fact reshapes another fact in ways nobody can explain in a meeting. Prefer single direction until you have a written reason for bi-directional and a test page that proves it.

Active vs inactive relationships

You might connect dim_date to both order date and ship date. Only one relationship can be active by default. The inactive one needs DAX (covered in Part 2) with USERELATIONSHIP when a measure should use ship date. Modeling both dates as separate role-playing date tables is another clean pattern. Pick one pattern and document it on the report’s “About this model” page.

Star schema diagram with fact_order_lines in the center connected to dim_date, dim_customer, and dim_product
Star schema diagram with fact_order_lines in the center connected to dim_date, dim_customer, and dim_product

What to put in Power Query versus the model

Power Query (Get Data / Transform) is for shaping tables before they land in the model: rename columns, set types, split columns, unpivot messy exports, filter out junk rows, create a proper date table. The model view is for relationships, hide fields from report view, mark a date table, and later measures.

A practical split:

  • In Power Query: clean keys, remove total rows that Excel added, ensure one header row, type numbers as numbers, build dim_date if the warehouse did not give you one.
  • In the model: create relationships, hide key columns from report view, set default summarization carefully, add measures (Part 2).
  • Not in either forever: business logic that belongs upstream in the warehouse or dbt. Power BI can patch, but patches multiply across workspaces.

If your company already transforms in dbt or a warehouse, connect to curated tables when you can. The star still matters in Power BI even when the warehouse already has a star. Your job becomes “wire the curated star correctly,” not “rebuild the universe from raw CSV.”

Worked example: a tiny sales starter model

Imagine a small retailer. Business questions for version one:

  • What was net revenue by month and product category?
  • How many orders did we take by region?
  • Who are the top customers by net revenue this year?

You do not need twenty dimensions. You need a fact at order-line grain and three dimensions.

Sample tables (toy data)

fact_order_lines (grain: one product line on one order):

order_line_idorder_idorder_date_keycustomer_keyproduct_keyquantitynet_amount
100150020260115C12P9240.00
100250020260115C12P3125.00
100350120260116C7P9480.00

dim_product:

product_keyproduct_namecategory
P3Notebook A5Stationery
P9Gel pen 0.5Stationery

dim_customer:

customer_keycustomer_nameregion
C7North Wind LLCWest
C12Blue Harbor CoEast

dim_date needs at least the days you will filter, with columns like date_key, date, year, month_name, year_month. Mark it as a date table in Power BI so time intelligence later has a chance to behave.

Relationships to create

  • dim_date[date_key] 1 → * fact_order_lines[order_date_key]
  • dim_customer[customer_key] 1 → * fact_order_lines[customer_key]
  • dim_product[product_key] 1 → * fact_order_lines[product_key]

Hide the key columns from report view so authors pick names and categories, not surrogate keys. Leave quantity and net_amount visible for now; in Part 2 you will prefer explicit measures over implicit column sums.

Sanity checks before any pretty page

Build a blank page with three card visuals only:

  • Sum of net_amount (expect 145.00 on the toy data)
  • Count of rows in fact_order_lines (expect 3)
  • Distinct count of order_id (expect 2)

Slice by Stationery category. Totals should still make sense. Slice by East region. You should see Blue Harbor’s two lines (65.00), not an inflated join. If numbers explode when you add a dimension, your relationship or grain is wrong. Fix that before you build a color theme.

Sanity check page showing net amount 145, three order lines, and two distinct orders with a category filter applied
Sanity check page showing net amount 145, three order lines, and two distinct orders with a category filter applied

A tiny DAX preview (optional now)

Even in Part 1, one explicit measure beats silent column defaults:

Net Revenue =
SUM ( fact_order_lines[net_amount] )

Put measures in a dedicated table or a clear folder later. For now, create the measure, hide the raw column from report view if your team agrees, and use the measure on the sanity cards. Part 2 expands this into question-shaped measures.

Import, DirectQuery, and “where does the model live?”

Starter projects usually use Import: Power BI loads a compressed copy into the dataset. Refresh updates that copy. Performance for small and medium models is often excellent. DirectQuery leaves data in the source and sends queries live. That helps when data must be near real time or too large to import, and it brings source latency and source permission problems into every click.

For learning the model-before-visuals habit, Import with a controlled extract is enough. What matters more than mode is whether your tables are modeled as a star with clear grain. A DirectQuery mess is still a mess. An Import star is teachable and testable.

Common mistakes

  • Many-to-many on dirty keys. Duplicate product keys in the dimension force many-to-many or auto relationship weirdness. Deduplicate dimensions first.
  • Using the auto date hierarchy on every date column. Hidden date tables multiply and confuse. Prefer one marked dim_date and relationships you can see.
  • Bi-directional filters as a default fix. They can mask a missing bridge table. Write the question that requires bi-di before you enable it.
  • Header metrics at line grain. Shipping, tax, or order-level discounts duplicated across lines without a compensating measure.
  • Leaving all columns visible. Authors pick the wrong field, invent two versions of “Region,” and the model becomes a junk drawer.
  • Skipping the blank sanity page. Pretty themes hide wrong totals until the exec meeting.
  • Building twenty pages before one trusted metric. Scope version one to a short question list. Expand after the star holds.

How to practice this week

  • Day 1: Pick one real report you use. Write three business questions it claims to answer. Write grain sentences for every table behind it (or for the export you can access).
  • Day 2: Rebuild a tiny star in a new Desktop file: one fact, date, and two dimensions. No themes. Only relationships and types.
  • Day 3: Create the three sanity cards. Break the model on purpose (duplicate a dimension key) and watch totals explode. Fix it. That scar is the lesson.
  • Day 4: Hide keys, rename fields for humans, add a one-paragraph “About this model” text box: grain, refresh source, owner.
  • Day 5: Only now sketch two visuals for one question. If a number disagrees with the sanity cards, the visual is guilty until proven innocent.

If your day job uses Tableau or Looker instead, the same order still holds: grain and relationships before chart cosmetics. Power BI’s Model view just makes the star harder to ignore, which is a gift if you use it.

Quick recap

  • Visuals are views. Wrong models produce confident wrong charts.
  • Star schema separates facts (events, measures) from dimensions (slices and labels).
  • Grain is a sentence you can defend in a meeting.
  • One-to-many from dimensions to facts, single filter direction, is the default starter pattern.
  • Sanity cards before themes. Explicit measures soon. Pretty pages last.

Next: Part 2 turns raw columns into measures that match the question, including filter context and a few DAX patterns you will reuse weekly.

Sources