There is a now a DAX query view in Power BI and let's get you started querying your data to get the analysis you are looking for!
DAX stands for Data Analysis eXpressions. I suppose DAE isn't as cool sounding as DAX! There is a whole site documenting it at https://learn.microsoft.com/dax/. And there are amazing books written about DAX and others sites maintained by the Power BI Community such as https://dax.guide.
So, what is a DAX query?
To help answer that, let's first answer what is a DAX formula?
When using Power BI you may have already been learning DAX through measures and calculated columns. These are DAX formulas.
Measures and calculated column DAX formula's return a scalar value, that is just a single value. This can be used with other columns to show that value per each value in this column too, which is showing that DAX formula "grouped by" the column's values. For example, Sales amount can be aggregated or summed up with a measure called Sales -- [Sales] = SUM('table'[Sales amount]). Then [Sales] can be grouped by Month to show the value by month. You get the idea.
Calculated tables are still DAX formulas but they return a table of values, such as a Date table. Calculated tables, calculated columns, and measures are all named objects of the semantic model. That is, they exist in the semantic model with a specific name and other properties that can be used in reports, and, of course the DAX queries that provide data to those reports.
DAX queries are the way reports (and anyone wanting data) get data and use those named DAX formulas from a semantic model. Just like in a SQL database you access the data through a SQL query, you can access the data in a semantic model through a DAX query. But DAX queries can do all that and much more. By much more, I mean you can have a DAX formula in the DAX query that does not exist in the semantic model. That is also what you are doing when you include a numeric data column in a Power BI report visual and the visual shows it summed up instead of just listing each value. In the DAX query the visual sends to get data from the semantic model, it has a DAX formula to SUM() the values.
I can view the visual DAX queries in Power BI Desktop to see this in action. To follow along, just open Power BI Desktop (for free in Windows Store) and use the sample data.
When I look at the Data pane, I can see summation symbols by the numeric columns. This indicates that they will automatically SUM if I use them in a visual, which will add the DAX formula SUM() in the visual DAX query. I can click on Sales and Product and a visual will automatically show summing, or aggregating, the Sales by Product.

I can use Performance Analyzer to see that DAX query it created. Click on Optimize ribbon > Performance Analyzer (for me it was already selected so I clicked it twice to show the pane). Then click Start recording and Refresh visuals. Expand the visual to see the option Run in DAX query view.

This will copy that DAX query and run it in DAX query view. And there we find that DAX formula being used to SUM the column for the visual!

Now, this DAX query is more complex than it needs to be, but that's because it's built for the visual, not for people. This structure is so when you change this to a pie chart, table, card, or whatever visual you decide, it can keep track of the data you wanted to see.
Here is the simple DAX query version of the same results.
EVALUATE
SUMMARIZECOLUMNS(
'financials'[Product],
"Sumv_Sales", SUM('financials'[ Sales])
)
ORDER BY [Sumv_Sales] DESC

The secret here is the first line. EVALUATE is required for all DAX queries to run. The second, and final requirement, is it must return a table of values, which is then called a result. This is similar to writing a calculated table, and you may have already been using SUMMARIZECOLUMNS shown here in a calculated table.
Now we are in DAX query view, let's check out the features of DAX queries.
The simplest DAX query you can create is EVALUATE and then the name of one of the tables in your model. If the table name has spaces you must use single quotes around the name, and it is optional to include the single quotes when the name doesn't have spaces. So here, let's look at the data in our table with EVALUATE financials:
EVALUATE financials

You can see down at the bottom how long it took to run this query, 122ms is but a flash on the screen, and how many results it returned as you can have as many EVALUATEs as you want returning results, and the shape of the result, in this case 16 columns wide and 700 rows long.
And here is the same query but this time I did use the single quotes around the table name, just to show it. All tables in a semantic model are denoted by the single quotes, so to get Intellisense to cooperate sometimes and just show you tables in your model, type the single quote to get the list.
EVALUATE 'financials'

Speaking of Intellisense, you can get a lot of helpful overlays when working with DAX query view but sometimes you don't really want that help, or overlay. You can always close them by hitting the ESC key. Holding down Shift or Ctrl will also allow you to press Enter without selecting anything in the Intellisense too, if it does have a preselection present.
Now I see the data, but maybe I don't want to see all this data. How do I look for just some of this data? FILTER can help. You may have used this one already in measures to limit what you wanted to sum over for part or all of a formula. This type of behavior is called context switching in a measure. For DAX query land it's pretty straightforward as you are wanting to see a subset of a table and to do that you filter it.
FILTER can be used to return rows where a value in a column matches to one value. Let's find only the data where the Country is Canada.
EVALUATE
FILTER(
'financials',
financials[Country] = "Canada"
)

Here I can wrap the entire table, here it is simply my financials table, in the FILTER function, and specify it to only return rows where the [Country] column is equal to "Canada".
Note: Power BI models are usually created without case sensitivity, so I could have put "canada" or "CANADA" there and found the same rows. But, a semantic model can be created with case sensitivity, and Direct Lake models created from a SQL endpoint usually are case sensitive, so keep that in mind. And Direct Query semantic models convert DAX to SQL and then run on the SQL database which could be case sensitive too. In a case sensitive model "canada", "Canada", and "CANADA" will only return rows where the case also matches.
You may also be thinking, wow you have that DAX query formatted so nice and neat. I actually wrote it in terribly but thankfully there is a Format query button I can press to clean it up for you!
My table here is one in my semantic model, but it's important to realize that anything that is a table would work, such as FITLER itself. I can wrap another filter around this FILTER if I wanted to add more filters on this table.
There are more elegant ways to FILTER. To look for more than one value to match to in a column I can use the IN operator. Let's look for Canada and Germany rows.
EVALUATE
FILTER(
'financials',
// financials[Country] ="Canada"
financials[Country] in {"Canada", "Germany"}
)

So this time I used the keyword IN and then gave a more than one value for it to match. This one is a bit tricky! Those are not regular parentheses, those are curly braces, {}, which in DAX query are really cool table constructors! I'll write a bit more about them later in this blog. This is the way to define more than one value to check by explicitly typing them in but you can also use a DAX formula that returns a table here too for it to dynamic list.
Also, you may notice I kept the earlier filter but it's now green with two slashes at the front. This is commented out. The DAX query will ignore that row, like I had deleted it, but I wanted to show you before and after next to each other. You can type them in or use the Comment and Uncomment buttons in the ribbon. You can type in helpful notes to yourself with these too.
Ok, so what if I wanted the other rows, where the countries are specifically not Canada or Germany? You can use the NOT function!
EVALUATE
FILTER(
'financials',
// financials[Country] ="Canada"
// financials[Country] in {"Canada", "Germany"}
NOT(financials[Country] in {"Canada", "Germany"})
)

The first time I tried to use NOT() I kept putting it in the wrong place. I tried in front of the {}, I tried in front of IN, but the correct way to use it is to wrap the whole thing in NOT(). I read it (now) as "Filter financials to not show rows where the country is in the provided table, which in this case is Canada and Germany." This helps me remember where not to put NOT() :).
You may be wondering if I can do any DAX formula there with filter and the answer is yes. It just needs result in true/false or include/exclude for each row of the table. Let's look for countries that only have 6 letters.
EVALUATE
FILTER(
'financials',
// financials[Country] ="Canada"
// financials[Country] in {"Canada", "Germany"}
// NOT(financials[Country] in {"Canada", "Germany"})
LEN(financials[Country]) = 6
)

This is similar to a where clause in SQL queries. Let's now see how I can filter by more than one column at once. I could have nested FILTERs. Such as filter first to Canada then filter that to only show Government.
EVALUATE
FILTER(
FILTER(
'financials',
financials[Country] = "Canada"
),
financials[Segment] = "Government"
)

Which gets the job done but it's a bit harder to understand and what if I wanted to instead make it return rows if either condition is true? The least typing approach is using && (and operator) to do the same as above.
EVALUATE
FILTER(
'financials',
[Country] = "Canada" && [Segment] = "Government"
)

Now I am back to just one FILTER and I can add as many columns as I want by using more and's using &&. I also realized I didn't need to specify the table in these cases as it's always using these columns from the specified table. So I can save even more typing!
To change this to include rows where either condition is met, that is include other countries as long as the segment is government, and to include other segments as long as the country is Canada, I can use or with || instead of &&.
EVALUATE
FILTER(
'financials',
[Country] = "Canada" || [Segment] = "Government"
)

You can even use parenthesis to get quite creative with your ands and or conditions with && and ||.
It is important to note there are AND() and OR() functions too, but you are limited to just two expressions and then you have to start nesting so the && and || notation is still is the least typing option. Use whatever method makes the most sense to you!
EVALUATE
FILTER(
'financials',
AND([Country] = "Canada", [Segment] = "Government")
)

Now I have shown you how to get results and filter them, let's see how we can just see specific columns of this table.
To specify and optionally rename them you can use SELECTCOLUMNS() on any table in the model or table output of a DAX expression.
EVALUATE
SELECTCOLUMNS(
'financials',
"New Segment Name", 'financials'[Segment],
// It is optional to rename!
'financials'[Country],
'financials'[Units Sold],
'financials'[Year]
)

SELECTCOLUMNS() does bring back all rows, including rows that are now the same as the columns with different values not showing. And it doesn't aggregate numeric columns as we did not specify any aggregation. I see each row's Units Sold amount.
I can add a SUM() DAX formula but it will act like a calculated column and do the SUM() on the whole table instead of the individual row. I can wrap it in a CALCULATE() and get the SUM() for the row but again, it's going to show me every row and not combine the rows I selected into one row and total the units sold.
EVALUATE
SELECTCOLUMNS(
'financials',
"New Segment Name", 'financials'[Segment],
// It is optional to rename!
'financials'[Country],
// 'financials'[Units Sold],
'financials'[Year],
"Units - SUM only", SUM(financials[Units Sold]),
"Units - SUM and CALCULATE", CALCULATE(SUM(financials[Units Sold]))
)

This is the expected behavior of SELECTCOLUMNS() as it's not grouping columns, and designed to simply limit the columns returned, just like if you had taken a table in Excel and deleted 5 columns. The other columns still have the same amount of rows, and may show duplicate rows.
Using SUMMARIZE() and SUMMARIZECOLUMNS() is where DAX queries become more powerful. These two functions that can take specific columns and use them as group by columns, that is de-duplicate the values so each row only has one combination of the selected rows. And DAX formulas, like the SUM(), will combine the multiple rows values for each set of specified columns. Let's first look at SUMMARIZE() and then more commonly used SUMMARIZECOLUMNS().
Let's look at SUMMARIZE(). We can change the example we were just looking at to use SUMMARIZE(). Note we cannot rename group by columns. To rename the group by column we would have to wrap SUMMARIZE() in SELECTCOLUMNS()!
EVALUATE
SUMMARIZE(
'financials',
'financials'[Segment],
'financials'[Country],
'financials'[Units Sold],
'financials'[Year]
// "Units - SUM only", SUM(financials[Units Sold]),
// "Units - SUM and CALCULATE", CALCULATE(SUM(financials[Units Sold]))
)

The row count has now dropped from 700 to 558, but units sold was not aggregated, there were just some duplicate rows where segment, country, units sold, and year were the same. To instead sum up the units sold by segment, country, and year we would add in a DAX formula SUM(). This column I must provide a name for the DAX query to work.
EVALUATE
SUMMARIZE(
'financials',
'financials'[Segment],
'financials'[Country],
// 'financials'[Units Sold],
'financials'[Year],
"Units - SUM only", SUM(financials[Units Sold]),
"Units - SUM and CALCULATE", CALCULATE(SUM(financials[Units Sold]))
)

I added the same SUM() DAX formula we used before in the SELECTCOLUMNS(), but this time it doesn't matter if there is a CALCULATE or not. The DAX formula is acting like a measure and taking into consideration the group by context of just by segment, country, and year. Now the rows are significantly reduced to just 50 rows, as that is the unique combination count of the three group by columns.
In SUMMARIZE() you specify the table, so I didn't need to specify the table on the columns, similar to SELECTCOLUMNS(). But, adding table to the column references may be required when there is more than one column with the same name. This can happen because I can bring in columns from other tables if there is a regular relationship between the tables, and the other table filters the table used. Instead of explaining all the nuances to that statement, just know that if it's not allowing you to add a column, then most likely the relationship between the tables is not suitable. I will add a Date table to the model to demonstrate this behavior working.

So now I can also use columns from the Date table.
EVALUATE
SUMMARIZE(
'financials',
[Segment],
'financials'[Country],
'financials'[Year],
'Date'[Year],
"Units", SUM(financials[Units Sold])
)

If I remove the table reference on either Year column, I get an error.

Ok, so let's try SUMMARIZECOLUMNS(). This one is my personal favorite!
SUMMARIZECOLUMNS() is the most versatile and least typing. I can do the previous example with SUMMARIZE() now without specifying the table. And I can pick columns from any table I like! Let's take a look.
EVALUATE
SUMMARIZECOLUMNS(
// 'financials',
'financials'[Segment],
'financials'[Country],
'Date'[Year],
"Units", SUM(financials[Units Sold])
)

What is also interesting about SUMMARIZECOLUMNS() is I can also use FILTER inside (and outside, before or after -- but let's focus on inside first!). Here I can filter to just show Canada again.
EVALUATE
SUMMARIZECOLUMNS(
'financials'[Segment],
'financials'[Country],
'Date'[Year],
FILTER(
VALUES(financials[Country]),
[Country] = "Canada"
),
"Units", SUM(financials[Units Sold])
)

Those of you with eagle eyes will notice I used VALUES() to limit my filter to a specific column. VALUES() simply returns the column specified as a table. I can do all the filter tricks above, and even have more than one filter to filter another table too. Here I want to filter the year too.
EVALUATE
SUMMARIZECOLUMNS(
'financials'[Segment],
'financials'[Country],
'Date'[Year],
FILTER(
VALUES(financials[Country]),
[Country] = "Canada"
),
FILTER('Date', [Year] = 2013),
"Units", SUM(financials[Units Sold])
)

And I can continue to build on this query adding additional DAX formulas, group by columns or filters.
I have covered many ways to get the data in a result table, so now let's look at how I can change the order in which they return or just get a set number of rows returned.
In the last SUMMARIZECOLUMNS() example I would like to order the results by the Units column and show it in descending order.
EVALUATE
SUMMARIZECOLUMNS(
'financials'[Segment],
'financials'[Country],
'Date'[Year],
FILTER(
VALUES(financials[Country]),
[Country] = "Canada"
),
FILTER('Date',[Year] = 2013),
"Units", SUM(financials[Units Sold])
)
ORDER BY [Units] DESC

And success, now I can see the government segment in Canada had the most units sold in 2013. The ORDER BY only sorts the results of the DAX query, which is why I can refer to the column by name even when it only exists in the DAX query itself. Columns not in the DAX query itself cannot be used in the ORDER BY. DAX formulas that can show a value for each row can also be used, but it's a bit tricky.
For example, I can order the rows by total profit for each of these group by columns. It is a little hard to validate as the total profit is not shown.
Note: That CALCUALTE is important here as it's more like calculated column behavior we saw in SELECTCOLUMNS().
EVALUATE
SUMMARIZECOLUMNS(
'financials'[Segment],
'financials'[Country],
'Date'[Year],
FILTER(
VALUES(financials[Country]),
[Country] = "Canada"
),
FILTER('Date',[Year] = 2013),
"Units", SUM(financials[Units Sold])
)
ORDER BY CALCULATE(SUM(financials[Profit])) DESC
A note on the sort by column property. In a semantic model, you can, optionally, set a column to sort by another column. Otherwise it defaults to being sorted by itself alphabetically. This is typically used in a Date table where I have "Month Name" as text. I don't want this to show as Apr, Aug, etc. So I have another column such as "Month of Year" as numeric from 1 to 12. When I sort "Month Name" by "Month of Year", now it will show as Jan, Feb, etc. This specified sort by behavior, or even the sorting by itself alphabetically, is ignored by a DAX query as I should sort the results with ORDER BY if a specific sort order is desired.
Let's look at units sold by month.
EVALUATE
SUMMARIZECOLUMNS(
'Date'[Month],
"Units", SUM(financials[Units Sold])
)

No order by is applied, so let's try and order by month.
EVALUATE
SUMMARIZECOLUMNS(
'Date'[Month],
"Units", SUM(financials[Units Sold])
)
order by 'Date'[Month]

As Month is a text column, it sorted alphabetically which is not what I wanted. I do have a column that has the correct month order a number and I did set this column to sort by that column so they would show in the calendar order, but it also didn't apply here. I try to order by the Month of Year column, but I get an error.

To sort by that column, the easiest way to resolve it is to add that column to the DAX query.
EVALUATE
SUMMARIZECOLUMNS(
'Date'[Month],
'Date'[Month of Year],
"Units", SUM(financials[Units Sold])
)
order by [Month of Year]

Now I finally see the results with the expected sort by order.
Using the TOPN() in a DAX query controls the number of rows returned, similar to a TOP in a SQL query. The difference to keep in mind in a DAX query is the TOPN() doesn't use the ORDER BY sort before picking the number of rows. TOPN() has it's own sort you can optionally specify. This makes it more performant and gives you more flexibility. Let's try with and with the sort specified in the TOPN().
EVALUATE
TOPN(
6,
SUMMARIZECOLUMNS(
'Date'[Month],
'Date'[Month of Year],
"Units", SUM(financials[Units Sold])
)
)
ORDER BY [Month of Year]

The results are limited to just 6 rows and the result is sorted by month of year, but it was not the first 6 months of the year returned! To do that, TOPN() needs that order specified.
EVALUATE
TOPN(
6,
SUMMARIZECOLUMNS(
'Date'[Month],
'Date'[Month of Year],
"Units", SUM(financials[Units Sold])
),
'Date'[Month of Year], ASC
)
ORDER BY [Month of Year] ASC

This may seem counter-intuitive at first, but it does allow for flexibility with DAX queries. For example, I can use TOPN() to return the last 6 months of the year, then use ORDER BY to order the months still in an ascending order.
EVALUATE
TOPN(
6,
SUMMARIZECOLUMNS(
'Date'[Month],
'Date'[Month of Year],
"Units", SUM(financials[Units Sold])
),
'Date'[Month of Year], DESC
)
ORDER BY [Month of Year] ASC

This example shows TOPN and ORDER BY using the same column, but they can use different columns or DAX formulas entirely.
One subtle thing to note is the TOPN() has a comma before you specify if it's ascending or descending sort, and ORDER BY does not have a comma.
Another way to accomplish this particular example is to use START AT with a DAX query.
EVALUATE
SUMMARIZECOLUMNS(
'Date'[Month],
'Date'[Month of Year],
"Units", SUM(financials[Units Sold])
)
ORDER BY [Month of Year] ASC
START AT 7

START AT here works with ORDER BY to begin at a specific value, with the value existing in the ORDER BY column specified.
Now let's get back to table constructors, the {} curly braces we used earlier. I call this the way to query anything!
Especially coming from using DAX in measures and calculated columns, I first wanted to see the value of my DAX formula or measure in a DAX query by simply writing EVALUATE SUM('financials'[Units Sold]). Which results in a error.

But what is not said there is it's very easy to convert anything to a table expression! You just wrap it in {} curly braces.
EVALUATE
{ SUM(financials[Units Sold]) }

You can also use these to simply type in a value.
EVALUATE
{ 1 }

Or a text in double quotes.
EVALUATE
{ "hello!" }

And with regular parenthesis you can specify multiple columns.
EVALUATE
{
(1, 2, 3)
}

And multiple rows with commas and parenthesis.
EVALUATE
{
(1, 2, 3),
(4, 5, 6),
(7, 8, 9)
}

By now you are probably wondering how can you change the name of those columns to something more meaningful. You can use SELECTCOLUMNS() but there is a function for this called DATATABLE().
Here is the more clunky SELECTCOLUMNS() approach.
EVALUATE
SELECTCOLUMNS(
{
(1, 2, 3),
(4, 5, 6),
(7, 8, 9)
},
"First", [Value1],
"Second", [Value2],
"Third", [Value3]
)

And here is with DATTABLE(). And now with DATATABLE(), there are some things to note. In addition to naming the columns upfront, you also specify the datatype. And, the columns are now also in {} curly braces instead of () parenthesis.
EVALUATE
DATATABLE(
"First",INTEGER,
"Second",INTEGER,
"Third",INTEGER,
{
{1,2,3},
{4,5,6},
{7,8,9}
}
)

Here it is with more data types.
EVALUATE
DATATABLE(
"First",INTEGER,
"Second",INTEGER,
"Third",INTEGER,
"Date", DATETIME,
"Comment", STRING,
{
{1,2,3,"1/1/2026", "January"},
{4,5,6, "1/3/2026", "March row"},
{7,8,9, "1/9/2026", "September!"}
}
)

What is interesting to note is that the DATATABLE() function does not work with DAX formulas. When I tried to do the Date column by using TODAY() it complained.

But you can totally do it in a regular table constructor. So, keep that in mind.
EVALUATE
SELECTCOLUMNS(
{
(1, 2, today()-53, "53 days ago"),
(4, 5, today()+53, "53 days from now"),
(7, 8, today()+20000, "20k from now?")
},
"First", [Value1],
"Second", [Value2],
"Third", [Value3],
"Comment", [Value4]
)

Hopefully this helps demystify what a DAX query is and how you can use it. I used Power BI Desktop to run through all these examples but you can use "Write DAX queries" option in the browser too on any published semantic model in your workspace too.
I published this PBIX to My workspace.

Clicked the link to Open it, which takes me to the report . From there I clicked on the dots and picked View semantic model.

And here I clicked Write DAX queries.

And I can run DAX queries just like I was in Power BI Desktop.

You can also use other tools such as SSMS, DAX Studio, Tabular Editor, and even Excel and Fabric notebooks. I'll get into the last two in another blog post.
Reach out to me on LinkedIn if you have any questions! And here are some links with more information.
Comments