Prepare Data · Topic 7 of 7

Query with KQL and DAX

Select, filter and aggregate data with KQL in an Eventhouse and with DAX queries in DAX query view: where, summarize, EVALUATE, SUMMARIZECOLUMNS.

  • 5 min
In a nutshell
  • KQL reads top to bottom: table, then where, extend, project, summarize ... by, order by, top.
  • bin() buckets time; ago() is relative to now; render timechart draws a chart.
  • KQL is case-sensitive; has is faster than contains; default join kind is innerunique.

What it is

KQL (Kusto Query Language) queries KQL databases in an Eventhouse. It is built for large volumes of time-stamped data such as logs and telemetry, and you write it in a KQL queryset or directly in the database.

DAX queries a semantic model. Besides writing measures, you can run whole DAX queries in DAX query view in Power BI Desktop or the Fabric web editor, or in tools such as DAX Studio over the XMLA endpoint.

EventhouseKQL querysetTime series resultsSemantic modelDAX query viewModel resultsmeasures and relationships

How it works

KQL: a pipeline of operators

A KQL query starts with a table and passes it through operators separated by |. Each operator works on the output of the one before.

SensorReadings
| where Timestamp > ago(1h)
| where DeviceType == "Thermostat"
| summarize AvgTemp = avg(Temperature), Readings = count()
    by DeviceId, bin(Timestamp, 5m)
| order by AvgTemp desc
SQL ideaKQL operator
SELECT col1, col2project col1, col2
Computed columnextend Total = Qty * Price
WHEREwhere
GROUP BY with aggregatessummarize sum(Amount) by Region
ORDER BYorder by (or sort by)
TOP 10top 10 by Amount desc, or take 10 for any 10 rows
COUNT(DISTINCT x)dcount(x), an estimate; count_distinct(x) is exact
JOINjoin kind=inner (OtherTable) on Key

bin(Timestamp, 5m) groups times into five-minute buckets, and ago(1h) means one hour before now. Adding | render timechart draws the result as a chart.

KQL details the exam likes

  • Names and the == operator are case-sensitive; =~ compares strings without case.
  • has matches a whole term and uses the index; contains matches any substring and is slower.
  • The default join kind is innerunique, which keeps only one row per key from the left table. Write kind=inner when you need every match.
Readings
| join kind=inner (Devices | project DeviceId, Building) on DeviceId
| summarize Readings = count() by Building
| top 5 by Readings desc

DAX queries

A DAX query starts with EVALUATE and must return a table. SUMMARIZECOLUMNS is the usual way to group and aggregate, and it is the function Power BI visuals generate.

EVALUATE
SUMMARIZECOLUMNS (
    'Product'[Category],
    FILTER ( VALUES ( 'Date'[Year] ), 'Date'[Year] = 2026 ),
    "Total Sales", [Total Sales]
)
ORDER BY [Total Sales] DESC

DEFINE adds measures or variables that exist only for the query:

DEFINE
    MEASURE Sales[Avg Order Value] =
        DIVIDE ( [Total Sales], DISTINCTCOUNT ( Sales[OrderID] ) )
EVALUATE
TOPN (
    5,
    SUMMARIZECOLUMNS (
        'Customer'[Customer Name],
        "Sales", [Total Sales],
        "AOV", [Avg Order Value]
    ),
    [Sales], DESC
)
ORDER BY [Sales] DESC

To return a single value, wrap it in a table constructor: EVALUATE { [Total Sales] }.

DAX query view

DAX query view lets you write and run queries, and right-click a table or measure for quick queries such as showing the top rows or defining all measures. A measure defined in a query can be added to the model from the query view, which makes it a safe place to test a measure first.

Because a DAX query runs against the model, it uses the model's relationships and measures, and row-level security applies to users who belong to a role. The same question asked in SQL against the warehouse would need its own joins and could give different totals if the measure logic differs.

Which language for which store

Data lives inQuery it with
Warehouse or SQL analytics endpointT-SQL or the Visual query editor
Eventhouse (KQL database)KQL
Semantic modelDAX
Lakehouse files and tablesSpark (PySpark or Spark SQL), or T-SQL for tables

A worked example

An operations team stores thermostat telemetry in an Eventhouse and has a semantic model of building costs.

  1. To find devices whose average temperature in the last hour is above 26, run where Timestamp > ago(1h), then summarize avg(Temperature) by DeviceId, then where on the average.
  2. To check a new "cost per square metre" measure before adding it to the model, define it in DAX query view and evaluate it in a SUMMARIZECOLUMNS query.
  3. When the numbers match the finance team's figures, add the measure to the model from the query view.
Exam tip

In KQL, where after summarize filters the aggregated rows, just as HAVING does in SQL.

How the exam asks about this

  • "Count events in 15-minute intervals over the last day." where Timestamp > ago(1d) | summarize count() by bin(Timestamp, 15m).
  • "Return every matching row from both tables in a KQL join." Use join kind=inner, not the default innerunique.
  • "Test a measure against the model without changing the model." Define it with DEFINE MEASURE in DAX query view.

What to remember for the exam

  • KQL reads top to bottom: table, then where, extend, project, summarize ... by, order by, top.
  • bin() buckets time; ago() is relative to now; render timechart draws a chart.
  • KQL is case-sensitive; has is faster than contains; default join kind is innerunique.
  • A DAX query starts with EVALUATE and returns a table; DEFINE MEASURE creates query-scoped measures.
  • SUMMARIZECOLUMNS groups and aggregates in DAX; DAX queries use the model's relationships, and RLS applies to role members.
Watch out

EVALUATE [Total Sales] is an error because a measure returns a scalar. Use EVALUATE { [Total Sales] } or ROW ( "Sales", [Total Sales] ).

Common mistakes

Using take when the question needs the top values. take returns any rows. top N by column sorts and returns the highest values.

Writing a KQL filter with the wrong case. where Country == "france" misses "France". Use =~ or match the exact case.

Querying the warehouse to check a model measure. SQL does not apply the model's measures or relationships. Test the measure with a DAX query against the semantic model.

Check yourself

  • Which KQL operator aggregates rows, and how do you group by time buckets?

    summarize, with bin(Timestamp, 5m) or another interval in the by clause.

  • Why might a KQL join return fewer rows than expected?

    The default join kind is innerunique, which keeps one left row per key. Use kind=inner to keep all matches.

  • What must a DAX query return?

    A table. EVALUATE needs a table expression, so wrap a single value in { } or ROW.

Topic 7 of 7 in Prepare Data

Test yourself on Prepare Data

The full Microsoft DP-600 question bank has 648 questions, with timed practice exams and an explanation for every answer.

This study guide is independent and is not affiliated with, endorsed by, or sponsored by Microsoft. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.