In this section
Module Summary
Module Summary
This module taught the five foundational concepts that govern how KQL processes data. Every query you write in every subsequent module builds on these five ideas.
Section 1.1 — The Tabular Data Model. Every KQL input and output is a table. A count returns a table with one column and one row. A summarize returns a table with grouping keys and aggregation results. An empty result returns a table with column headers and zero rows. There is no scalar mode, no single-value return. The tabular invariant means you can predict the output shape of any operator chain by tracing the table shape through each stage. You learned to use getschema to inspect column names and types, and take to examine actual values before writing queries.
Section 1.2 — The Query Pipeline. KQL operators execute sequentially, top to bottom, exactly as written. Each operator consumes the previous operator's tabular output and produces a new table. Operator order determines both results and performance. Placing where filters before summarize reduces the data volume the aggregation operates on. Columns disappear after summarize unless they appear in the by clause or an aggregation function. The let statement names intermediate tables for readability and join preparation. Incremental query construction (two operators, verify, two more, verify) is faster than building a complete query and debugging it after the fact.
Section 1.3 — Data Types in Depth. Nine data types appear in security tables: string, datetime, dynamic, bool, int, long, real, timespan, and guid. String is the most common and the most deceptive because fields like ResultType store numeric codes as text. Dynamic contains the richest investigation data (location, device, authentication method, CA policy results) and requires tostring() casts before comparison. The implicit type hierarchy (int → long → real → decimal) governs automatic promotion in arithmetic. KQL does not warn about type mismatches between string and integer.
Section 1.4 — Type Conversion and Coercion. Every to* conversion function returns null on failure. Null then propagates silently through every downstream operation. The conversion tracking pattern (countif(isnotnull(...))) verifies how many rows a conversion would silently drop. parse_json() converts string-encoded JSON into queryable dynamic objects. coalesce() provides fallback values for missing data. The case function translates opaque error codes into human-readable descriptions for investigation reports.
Section 1.5 — Null Handling. Null fails every comparison, including not-equal. A where Field != "value" clause silently excludes null rows alongside matching rows. In security investigations, adversary activity can hide in null fields when the attacker uses authentication flows that do not populate location, device, or IP data. The defensive pattern is where Field != "value" or isempty(Field). The null audit query reveals which columns have significant null rates. Every detection rule should document its null-handling decisions for every filtering column.
What's next
Module 2 introduces the operators that filter, transform, and aggregate data: where, extend, project, summarize, sort by, top, and distinct. You will use the tabular model, pipeline ordering, type awareness, and null handling from this module in every query you write. The five concepts taught here are not background theory. They are the working foundation for every investigation and detection engineering task in this course.
How was this module?
Your feedback helps us improve the course. One click is enough — comments are optional.