Why You Should Use COUNTROWS Instead of COUNT in Power BI DAX
Why You Should Use COUNTROWS Instead of COUNT in Power BI DAX
If you've spent any time writing DAX, you've probably used COUNT at some point. It feels natural - you want to count things, so you reach for COUNT. But there's a better alternative sitting right there in the function list that most people skip over: COUNTROWS.
This might seem like a minor style preference. It's not. The difference between COUNT and COUNTROWS affects readability, maintainability, and in some cases, performance. After reviewing hundreds of Power BI models across Australian organisations, I can say that teams who default to COUNTROWS write cleaner, more predictable DAX.
Here's why, and what the practical difference looks like.
What COUNT Actually Does
COUNT takes a single column reference and counts the number of non-blank numeric values in that column. That's it. It operates on one column at a time.
Order Count = COUNT(Sales[OrderID])
Seems straightforward enough. But there are some quirks that trip people up.
First, COUNT only counts numeric values. If you pass it a text column, it won't count text values - you'd need COUNTA for that. This distinction catches people out constantly. Someone writes COUNT(Customers[CustomerName]) expecting a count of customers, gets zero, and spends twenty minutes debugging before realising they needed COUNTA.
Second, COUNT skips blanks. If your column has blank values, COUNT won't include those rows. Sometimes that's what you want. Often it isn't, and you don't even notice the discrepancy until someone questions why your dashboard total doesn't match the source system.
Third - and this is the real problem - COUNT forces you to pick a column. Which column? It doesn't matter for counting rows, but now you've introduced a dependency on a specific column that has nothing to do with what you're actually trying to measure.
What COUNTROWS Does Better
COUNTROWS takes a table expression and counts the number of rows in it. No column selection required.
Order Count = COUNTROWS(Sales)
That's cleaner. It says exactly what it means: count the rows in the Sales table. No ambiguity about which column, no questions about whether blanks are being handled, no confusion about numeric vs text.
When used inside an iterator or with CALCULATE, COUNTROWS works on whatever filter context is active:
Orders This Year = CALCULATE(
COUNTROWS(Sales),
DATESYTD('Date'[Date])
)
You're counting rows. Full stop. The intent is obvious to anyone reading the measure, including your future self six months from now when you've forgotten why you wrote it.
The Performance Angle
In most real-world models, the performance difference between COUNT and COUNTROWS is negligible. The VertiPaq engine is smart enough to optimise both patterns efficiently.
But there's an indirect performance benefit that matters more than raw execution speed. When you use COUNT on a column, the engine has to resolve that column reference and check for blanks. When you use COUNTROWS on a table, the engine can take a more direct path - it knows you want a row count and can optimise accordingly.
Where this becomes more noticeable is in complex measures with multiple CALCULATE statements and filter modifications. Every column reference is a potential point where the engine needs to do extra work. COUNTROWS avoids this by working at the table level.
The bigger win, though, is maintainability. Measures using COUNTROWS are easier to refactor. If you rename a column, every COUNT reference to that column breaks. COUNTROWS references the table, which is far less likely to change.
Common Patterns Where COUNTROWS Wins
Counting with filters
High Value Orders = CALCULATE(
COUNTROWS(Sales),
Sales[Amount] > 1000
)
Compare that to:
High Value Orders = CALCULATE(
COUNT(Sales[OrderID]),
Sales[Amount] > 1000
)
The first version is cleaner. The OrderID column reference in the second version is just noise - it has nothing to do with what you're measuring.
Counting distinct values
When you need distinct counts, COUNTROWS pairs naturally with DISTINCT or VALUES:
Unique Customers = COUNTROWS(
DISTINCT(Sales[CustomerID])
)
This is more explicit than DISTINCTCOUNT, which hides what's happening. Both work, but COUNTROWS + DISTINCT makes the logic visible.
Counting across relationships
Products Sold = COUNTROWS(
RELATEDTABLE(Products)
)
This counts the products related to the current row context. Try doing that cleanly with COUNT and you'll end up with something much less readable.
When COUNT Is Still Appropriate
I'm not saying COUNT should never appear in your DAX. There are legitimate cases.
If you specifically want to count non-blank numeric values in a column - not count rows, but count values - then COUNT is the right function. It's doing exactly what its name suggests.
Similarly, COUNTA (which counts non-blank values regardless of data type) has its place when you're checking for data completeness. "How many rows have a phone number filled in?" is a valid COUNTA question.
The key distinction is intent. If you're counting rows, use COUNTROWS. If you're counting values in a specific column, use COUNT or COUNTA. The function should match what you're actually trying to measure.
Refactoring Existing Measures
If you're working with an existing model that uses COUNT everywhere, you don't need to drop everything and refactor. But as you touch measures for other reasons - bug fixes, new requirements, performance tuning - swap COUNT for COUNTROWS where appropriate.
A quick audit approach: search your model for COUNT( and review each instance. Ask yourself, "Am I counting rows or counting column values?" If it's rows, switch to COUNTROWS. If it's genuinely about values in a specific column, leave it.
In DAX Studio you can export all measures and do this review in bulk. For a model with 50+ measures, that's much faster than clicking through each one in Power BI Desktop.
What This Looks Like In Practice
We worked with a financial services client last year whose Power BI model had about 200 measures. Roughly 60 of them used COUNT on an ID column when they really just wanted a row count. During a model review, we refactored those to COUNTROWS. The functional behaviour didn't change - the numbers were identical. But the DAX became noticeably easier to read and maintain.
Three months later, when the same team needed to add new measures building on the existing ones, they reported that understanding the model was significantly easier. That's the real payoff. Not milliseconds saved on query execution, but hours saved on comprehension and debugging.
Building Good DAX Habits
Small decisions like COUNT vs COUNTROWS compound across a model. One measure, one function choice - who cares? But multiply that across hundreds of measures, dozens of reports, and a team of analysts who need to understand each other's work, and these habits start to matter.
Microsoft's own Power BI DAX best practices guidance recommends COUNTROWS as the default for counting rows. It's not just our opinion - it's the recommended pattern from the team that builds the product.
If your organisation is building out Power BI reporting and wants to make sure the foundation is solid, our Power BI consultants review models for exactly these kinds of patterns. Good DAX habits established early save real time and frustration down the track.
And if you're thinking about the bigger picture - how reporting fits into your data strategy, whether Fabric or Azure makes sense for your data platform - our business intelligence team can help map that out.
Start with COUNTROWS. It's a small change that makes your DAX better.