The first real Power BI report I built ran on one giant table. I had poured sales rows, product names, regions, customer cities and dates into a single sheet because that felt like the honest thing to do, keep everything in one place where I could see it all at once. It opened slow, the refresh crawled while I waited, and then a regional total came back almost double what the finance sheet said. I spent a whole evening hunting for a formula bug that was never there.

The problem was the shape of the data, not the math. I had built one flat table when the data really wanted to be a small solar system, one table of things that happened in the middle and a ring of small tables around it describing who, what and when. That shape has a name, the star schema, and learning it was the single biggest jump I made in Power BI.

The flat table felt honest. It was quietly lying to me every time I added up a column.
01/THE FLAT TABLE

Why one big table feels right and quietly breaks

A flat table feels right because everything sits in front of you. One row per sale, and every column you could ever want to filter by is right there on that row. The trouble starts because the row has to carry a full copy of everything. If a region made fifty thousand sales, the word NORTH is stored fifty thousand times, next to the category, the city and the salesperson, over and over. The file swells, the model gets heavy, and the refresh you run every morning slowly gets slower.

The redundancy is the visible cost. The quiet one is worse. Once a value repeats down a column, any total that touches a related table can start counting the same money more than once, and the report still looks calm and confident while it does it. That doubled regional total I chased all evening was not a broken formula, it was a flat shape doing exactly what a flat shape does.

ONE BIG TABLESALEAMTREGION1100NORTH2100NORTH390NORTH4120NORTH580NORTH6110NORTHsame label storedon every rowRESHAPESTAR SCHEMAPRODUCTDATEREGIONCUSTOMERSALESFACT
fig 01 · one flat table on the left, the same data as a star on the right

So the fix is not a cleverer measure or a faster laptop. The fix is to stop storing the description of a thing next to every event about it, and instead pull those descriptions out into their own small tables. That is the whole idea, and the next section is just how you actually cut the big table apart.

02/FACTS AND DIMENSIONS

Split it into things that happened and things that describe them

Every table you end up with is one of two kinds. A fact table holds the things that happened, one row for each sale, each shipment, each meter reading, and it carries the numbers you add up, the amount and the quantity, plus a few short ID codes that point outward. Keep it narrow and let it grow tall. A dimension table holds the who, the what and the when, one row per product, one per customer, one per region, one per date, and it carries the labels you slice and filter by. Those stay short and can be a little wider.

A quick test for which is which

If you add it up, it belongs in the fact table. If you group by it or filter on it, it belongs in a dimension. Amount is a fact because you sum it. Region is a dimension because you slice by it. That one question sorted almost every column when I cut my flat sheet apart, the sales numbers stayed in the middle as the fact, and product, region, customer and date each moved out into their own little table with the repeats collapsed down to one clean row each.

One thing to do before any of this, get the source clean, because a star built on messy inputs just hides the mess in a prettier shape. I wrote separately about how I clean a messy Excel file before it ever reaches a model, and that step pays for itself here.

03/ONE TO MANY

Relationships and the single direction rule

With the tables split, you wire each dimension to the fact with a relationship. One region row relates to many sales rows, one product to many sales, one date to many sales. That is called a one to many relationship and it is the backbone of the whole model. The one side is the dimension, the many side is the fact, and Power BI wants it that way.

Here is the part people get wrong, and it is the same trap that doubled my total. A relationship also decides which way a filter travels, and by default you want it to flow one way only, from the dimension down into the fact. When you click North on a region slicer, that filter should run into the sales and stop. Turning on both directions feels helpful and it is where quiet double counting is born, because the filter starts bouncing back and forth and the same rows get counted again.

FILTER FLOWS ONE WAYREGIONAMOUNTNORTH100NORTH100TRUE TOTAL200BOTH DIRECTIONS ONREGIONAMOUNTNORTH100NORTH100NORTH100NORTH100SHOWN TOTAL400same two sales, one wrong setting, twice the number
fig 02 · the same two sales, correct on the left, double counted on the right

Leave every relationship as single direction until you have a real reason not to, and most models never need the exception. Once the wiring is clean, your numbers stop arguing with each other and your measures behave, which is exactly when writing them gets easy. I go deeper on that in how DAX measures actually work, and the same modelling choices decide what Power BI Copilot will and will not build for you.

04/THE DATE TABLE

Give time its own table, then hand off the rest

There is one dimension almost everyone skips at first, and it is the one that pays off the most. Do not lean on the date column sitting inside your fact table. Build a separate date table with one row for every single day across your whole range, with no gaps, then tell Power BI to mark it as the date table.

The reason is simple once you see it. Any time based math, a running year to date, this month against the same month last year, needs an unbroken run of days to count across. Your fact only has the dates where something actually happened, so it is full of holes, weekends and quiet weeks missing. A dedicated date table fills those holes and lets one calendar drive every fact you own, sales, targets and shipments all at once.

DATE TABLEone row per day, no gapsSALESTARGETSSHIPMENTSmark it as the date table, then year to date math just works
fig 03 · one marked date table feeding down into every fact

So the two moves you can make today are these. Split your one big table into a fact plus a few dimensions, and add a real date table wired in as single direction. That alone will make your model lighter and your totals honest. Once the shape is right, building the actual report on top is the easy part, and I walk through that in building an executive dashboard people actually read.

The part I am holding back is the judgment call for your specific data, which columns really deserve their own dimension, when a snowflake is worth it, how to model a messy source that fights the star, and how to tune the whole thing so a refresh that took minutes takes seconds. That is the build I do for clients, and it is shaped around your files, not a generic diagram. If you have a report that is slow or a total you do not trust, start a project and tell me what it looks like, even if the honest answer is that your model is already fine.

> Where this leaves you

So the whole answer, in the order I would do it. See the flat table for the trap it is, then cut it into a fact table of the things that happened and a ring of dimension tables that describe them. Wire each dimension to the fact as one to many, keep the filter flowing one direction so nothing gets counted twice, and give time its own marked date table so the year over year math has room to work. That shape is why my reports stopped being slow and quietly wrong.

If your model needs more than a diagram can give, the done for you version is the one I build around your real data, tuned until the refresh is quick and every total ties out. And if you want the tools I ship along the way, the membership here includes every product I release, now and later, licensed to you forever. Get the shape right first, and the dashboard almost builds itself.