KQL Tutorial for Microsoft Fabric: Eventhouse & Real-Time Intelligence
KQL (Kusto Query Language) is the query language for every Fabric Eventhouse. If you use Eventstream, build real-time dashboards, or work with IoT and log data in Fabric, KQL is what you write to get answers. This guide covers the pipe syntax from scratch, every operator you’ll use in production, time series analysis, anomaly detection, materialized views, update policies, and 8 real-world query patterns you can use immediately.
KQL (Kusto Query Language) is the query language for Microsoft Fabric Eventhouse and KQL Databases. It uses a pipe-based syntax where data flows through a sequence of operators: TableName | where condition | summarize aggregation. KQL is optimized for time-series data, log analytics, telemetry, and high-volume streaming data. In Fabric, KQL queries data ingested from Eventstream sources — Event Hubs, Kafka, IoT Hub, MQTT — and is also tested on the DP-700 exam under Domain 2 (Ingest and Transform Data). (per Microsoft Learn — KQL Overview)
The Eventhouse — Where KQL Lives in Microsoft Fabric
Before writing a single KQL query, you need to understand what you are querying. In Microsoft Fabric, Eventhouse is the analytical engine for the Real-Time Intelligence workload. It is the equivalent of a database server — but designed specifically for time-series and event-based data at high ingestion rates.
Each Eventhouse contains one or more KQL Databases. Each KQL Database contains tables, materialized views, stored functions, update policies, ingestion mappings, and OneLake shortcuts. When you create an Eventhouse in Fabric, it typically auto-creates a KQL Database with the same name — your starting point for writing queries.
Per Microsoft Learn, Eventhouse is designed for any scenario that includes event-based data: telemetry and log data, time series and IoT data, security and compliance logs, and financial records.
What Makes Eventhouse Different from a SQL Database
- Append-only tables: KQL tables receive new records continuously. Old data is rarely updated. This design supports massive ingestion rates efficiently.
- Sub-second queries at scale: KQL queries across billions of rows return results in seconds. The engine uses columnar storage, compression, and indexing tuned for time-based filtering.
- Time-based indexing: Every KQL table has an ingestion time column automatically. Time-based filters like
ago(1h)are highly optimized. - Auto-refresh materialized views: Pre-computed aggregations update automatically as new data arrives — no manual refresh or pipeline needed.
- OneLake Availability: Enable at the table or database level to mirror KQL data to OneLake in Delta-Parquet format — making it queryable by Spark, Warehouse, and Power BI Direct Lake.
Eventhouse + Eventstream — The Complete RTI Stack
The standard Fabric RTI pattern: Eventstream ingests streaming data from sources (Event Hubs, Kafka, IoT Hub, MQTT) and routes events to an Eventhouse KQL Database destination. From there, KQL queries the landed data. Data Activator monitors the stream for conditions and fires alerts. Power BI connects to the Eventhouse for real-time dashboards. All four components work together — Eventstream handles ingestion, Eventhouse handles storage and querying, Data Activator handles alerting, Power BI handles visualization.
KQL Pipe Syntax — How Every Query Is Structured
KQL uses a pipe-based syntax. Every query starts with a table name and then pipes the result through a sequence of operators. Each operator receives the output of the previous one and transforms it. You read a KQL query left-to-right (or top-to-bottom) like a recipe: start with the data, filter it, reshape it, aggregate it, render it.
Key Syntax Rules
- Table name first, no FROM: Unlike SQL, you don’t write
FROM TableName SELECT. The table name starts the query. - Pipe separates operators: The
|character passes the result of one operator to the next. - Case-sensitive identifiers: Column names and table names are case-sensitive.
Temperatureandtemperatureare different columns. - Comments: Single-line comments use
//. Block comments use/* */. - let statements: Define variables or subqueries with
letat the top of the query. Think of them as CTEs in SQL. - String literals: Use single quotes
'value'or double quotes"value".
KQL vs SQL — Side-by-Side Comparison for Fabric Engineers
If you already know SQL, the fastest way to understand KQL is to see them side by side. The mental model shift: in SQL you declare the structure of the result upfront. In KQL you describe the transformations the data goes through, step by step.
| Operation | SQL | KQL |
|---|---|---|
| Select all rows | SELECT * FROM Sensors | Sensors |
| Filter rows | SELECT * FROM Sensors WHERE Temp > 80 | Sensors | where Temp > 80 |
| Select columns | SELECT DeviceId, Temp FROM Sensors | Sensors | project DeviceId, Temp |
| Add a column | SELECT *, Temp * 1.8 + 32 AS TempF FROM Sensors | Sensors | extend TempF = Temp * 1.8 + 32 |
| Count rows | SELECT COUNT(*) FROM Sensors | Sensors | count |
| Group and aggregate | SELECT DeviceId, AVG(Temp) FROM Sensors GROUP BY DeviceId | Sensors | summarize AvgTemp = avg(Temp) by DeviceId |
| Sort results | ORDER BY Temp DESC | | order by Temp desc |
| Limit rows | TOP 10 / LIMIT 10 | | take 10 |
| Filter last 1 hour | WHERE Timestamp > DATEADD(HOUR,-1,GETDATE()) | | where Timestamp > ago(1h) |
| Time buckets | DATEPART(MINUTE, Timestamp)/5*5 (awkward) | | summarize … by bin(Timestamp, 5m) |
| Like / contains | WHERE Message LIKE ‘%error%’ | | where Message contains “error” |
| Starts with | WHERE DeviceId LIKE ‘TEMP%’ | | where DeviceId startswith “TEMP” |
| Distinct count | COUNT(DISTINCT DeviceId) | dcount(DeviceId) in summarize |
| Inner join | JOIN TableB ON a.Key = b.Key | | join kind=inner TableB on Key |
Performance Tip — has vs contains for String Filtering
In KQL, has is significantly faster than contains for string filtering. has performs a whole-term search using an inverted index. contains performs a substring search that scans every row. Use has "error" when searching for a whole word. Use contains "err" only when you need partial string matching and there is no alternative. This performance difference is critical at billion-row scale.
Core KQL Operators — The Complete Reference for Fabric
where
Filters rows matching a condition. Supports: comparison operators, string operators (has, contains, startswith, matches regex), boolean operators (and, or, not), and in lists.
project
Selects specific columns, renames them, or creates new calculated columns. Drops all other columns — think SELECT with an explicit column list. Use project-away to drop specific columns and keep the rest.
extend
Adds new calculated columns without dropping existing ones. The KQL equivalent of SQL computed columns or SELECT *, new_col. Use for unit conversions, derived values, and string parsing.
summarize
Groups rows and computes aggregations. Equivalent to SQL GROUP BY. Aggregation functions: count(), sum(), avg(), min(), max(), dcount(), percentile(), make_list(), make_set(), arg_max(), arg_min().
bin
Rounds values down to the nearest multiple — used in summarize by bin(Timestamp, 5m) to create time buckets. Essential for time series aggregations. Works with timespan values: 1m, 5m, 1h, 1d.
ago
Returns a datetime relative to now. ago(1h) = one hour ago. ago(7d) = seven days ago. Used almost universally in real-time queries: | where Timestamp > ago(1h). Combine with between for time range queries.
join
Combines rows from two tables on a matching key. Join kinds: inner, leftouter, rightouter, fullouter, leftsemi, leftanti. For dimension enrichment, use lookup instead — it is optimized for enriching a large stream with a small dimension table.
render
Generates an inline visualization in the KQL queryset. Useful for exploration: | render timechart, | render barchart, | render piechart. Not used in production queries sent to Power BI or Data Activator — for exploration in the Fabric queryset only.
String Operators — Choosing the Right One
| Operator | What It Does | Case-Sensitive? | Performance |
|---|---|---|---|
| has | Whole-word term match | No (has_cs for yes) | Fastest — uses inverted index |
| contains | Substring match anywhere | No (contains_cs for yes) | Slow — full scan |
| startswith | String starts with value | No (startswith_cs for yes) | Fast — prefix index |
| endswith | String ends with value | No | Medium |
| matches regex | Regular expression match | Yes | Slowest — use as last resort |
| in | Value in a list | No (in~ for case-insensitive) | Fast for small lists |
Time Series Analysis — KQL’s Most Powerful Capability
Time series analysis is where KQL dramatically outperforms SQL. Built-in functions for binning, sliding windows, anomaly detection, and forecasting are native to the language — in SQL these require multiple CTEs, window functions, and custom implementations.
Time-Based Filtering
Time Series Aggregation with bin()
make-series — Time Series Arrays for Advanced Analysis
The make-series operator creates a time series array from a set of values — one array per group, one element per time step. This is the input format for KQL’s built-in ML functions: anomaly detection, forecasting, and trend decomposition.
series_decompose_anomalies — How It Works
This function decomposes a time series into trend, seasonality, and residual components, then identifies points where the residual exceeds a threshold (default 1.5 standard deviations). It returns a score per time step: 1 (high anomaly), -1 (low anomaly), 0 (normal). No training data required — it learns the pattern from the series itself. Use it in Eventstream-fed Eventhouse tables for automated IoT anomaly detection without a separate ML pipeline.
Forecasting with series_decompose_forecast()
Joins and Lookups — Combining Tables in KQL
KQL supports all standard join types but the syntax differs from SQL. The key practical difference: the left table in a KQL join should be the larger table (your event stream), and the right table should be the smaller table (your dimension/enrichment data). This is the opposite of some SQL convention instincts.
| Join Kind | What It Returns | SQL Equivalent |
|---|---|---|
| inner | Rows that match in both tables | INNER JOIN |
| leftouter | All left rows, nulls for right non-matches | LEFT OUTER JOIN |
| rightouter | All right rows, nulls for left non-matches | RIGHT OUTER JOIN |
| fullouter | All rows from both tables | FULL OUTER JOIN |
| leftsemi | Left rows that have a match in right (only left columns) | WHERE EXISTS |
| leftanti | Left rows that have NO match in right | WHERE NOT EXISTS |
| lookup | Broadcast-optimized enrichment — large left + small right | Optimized LEFT JOIN |
Materialized Views, Update Policies & Stored Functions
Materialized Views — Pre-Computed Aggregations That Auto-Update
Per Microsoft Learn, materialized views expose an aggregation query over a source table and automatically update as new data arrives. They consist of two parts: a materialized part (pre-computed results from already-processed data) and a delta (new data since the last background update). When you query a materialized view, the system combines both parts to return always-current results.
Materialized views encapsulate a single summarize statement. They are the Gold layer of a KQL-native Medallion Architecture — Silver is the raw ingested table, Gold is the materialized view with the business aggregation pre-computed.
Querying a materialized view is identical to querying a table:
backfill=true — Populate from Historical Data
When you create a materialized view with backfill=true, Fabric processes existing historical data in the source table and pre-computes results for all past time periods — not just future ingestion. Without backfill, the view starts empty and builds from the next ingested event forward. Use backfill when you need historical coverage from day one. Be aware that backfill can take time on large tables.
Update Policies — Transform Data at Ingestion Time
An update policy runs a KQL function automatically every time new data is ingested into the source table. The function’s output is appended to a target table. This is the KQL equivalent of a Silver transformation — raw events land in the Bronze table, the update policy transforms them and writes normalized rows to the Silver table, all within the Eventhouse without any external pipeline.
Stored Functions — Reusable KQL Queries
Stored functions in Eventhouse are named, parameterized KQL expressions stored in the KQL Database. Call them like a table reference or with parameters. Useful for standard query patterns that your team runs repeatedly — device health checks, error rate calculations, and SLA evaluations.
8 Real-World KQL Query Patterns for Fabric Eventhouse
Pattern 1 — Latest Reading Per Device
Get the most recent event for each device — the “current state” query. Common for IoT dashboards showing live sensor values.
Pattern 2 — Error Rate Over Time
Calculate the percentage of error events per time bucket. Common for application log monitoring and SLA dashboards.
Pattern 3 — Top N by Metric
Find the top N entities by a metric in a time window. Common for identifying highest-consuming devices, top error sources, and peak users.
Pattern 4 — Week-Over-Week Comparison
Compare current week values against the same period last week. Common for business reporting and trend detection.
OneLake Availability — Bridge Eventhouse Data to the Rest of Fabric
Per Microsoft Learn: OneLake Availability can be enabled at the database or table level in Eventhouse. When enabled, it mirrors KQL table data to OneLake in Delta-Parquet format — making it queryable by other Fabric engines without copying or duplicating data.
What OneLake Availability Enables
- Power BI Direct Lake: Connect a semantic model directly to Delta files mirrored from Eventhouse — no import refresh, no DirectQuery overhead, always-current data
- Fabric Warehouse: Run T-SQL queries over Eventhouse data via an OneLake shortcut pointing to the mirrored Delta files
- Fabric Lakehouse: Join Eventhouse data with Lakehouse tables in a Spark Notebook — real-time events + historical batch data in a single PySpark query
- Medallion in Eventhouse: Materialized views in Eventhouse with OneLake Availability enabled create a KQL-native Gold layer that Power BI can read via Direct Lake
Enable OneLake Availability
In the Fabric portal, open your Eventhouse → navigate to the KQL Database → open the database settings → enable OneLake Availability at the database level (covers all tables) or per table. After enabling, the data is mirrored to a Delta Lake location in OneLake. The location appears in the database properties and can be referenced as a Lakehouse shortcut.
OneLake Availability Adds Storage Cost
Enabling OneLake Availability creates a Delta-Parquet copy of your KQL data in OneLake. This doubles the storage footprint for that table — the original Eventhouse column store plus the Delta-Parquet copy in OneLake. For large tables (hundreds of GB+), evaluate whether the cross-engine access benefit justifies the additional storage cost. OneLake storage is billed at $0.023/GB/month.
The combination of Eventstream → Eventhouse → OneLake Availability → Direct Lake creates a complete real-time analytics pipeline where data ingested from IoT sensors appears in Power BI reports within seconds — without a scheduled refresh, without data duplication at the semantic model layer, and without any ETL pipeline between the event and the dashboard. This is the architecture that replaces traditional streaming → data lake → ETL → reporting pipelines for time-series workloads.
KQL and the DP-700 Exam — What You Need to Know
KQL is tested on the DP-700 exam under Domain 2: Ingest and Transform Data (30–35%). The exam does not expect advanced KQL mastery — it tests whether you can use KQL for practical Eventhouse scenarios. These are the specific skills tested:
| DP-700 Skill | What It Means in Practice | Key KQL Operator |
|---|---|---|
| Select, filter, and aggregate data using KQL | Write basic KQL queries: where, project, summarize, order by, take | where, summarize, project |
| Discover data using Real-Time Hub | Navigate the Real-Time Hub to find Eventstream and Eventhouse items | N/A — UI navigation |
| Ingest or access data as needed | Configure Eventstream → Eventhouse KQL Database destination | N/A — portal configuration |
| Implement OneLake integration for Eventhouse | Enable OneLake Availability on KQL Database or tables | N/A — portal configuration |
| Choose between different data stores | Know when to use Eventhouse (time series, streaming) vs Lakehouse (batch) vs Warehouse (SQL analytics) | N/A — architectural decision |
DP-700 KQL Exam Focus — What to Prioritize
- Know the pipe syntax: table | where | summarize | project | order by | take
- Know
ago()andbin()for time-based queries — these appear in almost every RTI exam scenario - Know when Eventhouse is the right data store (streaming, time-series, IoT) vs when Lakehouse (batch Delta) or Warehouse (SQL) is correct
- Know that KQL tables are append-only and ingestion time is automatically tracked
- Know how to configure Eventstream → Eventhouse as a destination (the portal steps, not the KQL)
- Materialized views, update policies, and anomaly detection are NOT required for DP-700 — they appear in DP-600 Real-Time Intelligence topics
For the complete DP-700 study plan covering all three domains with a 6-week schedule, see our DP-700 Exam Guide. For interview questions covering Real-Time Intelligence and Eventhouse scenarios, see our Real-Time Intelligence Interview Questions.
FAQ – KQL Tutorial for Microsoft Fabric
Official References and Related UIG Guides
Official Microsoft Documentation
Related UIG Data Lab Guides
⚠️ Accuracy Disclaimer
All KQL syntax, Eventhouse features, materialized view behavior, OneLake Availability details, and throttling levels are verified against Microsoft Learn KQL Overview, Eventhouse Overview, and Query Data in KQL Database training module as of June 2026. KQL and Eventhouse capabilities update frequently — verify current syntax and feature availability on Microsoft Learn before production implementation. UIG Data Lab is an independent publication, not affiliated with or endorsed by Microsoft Corporation.