DQL Essentials Skill
DQL is a pipeline-based query language. Queries chain commands with | to filter, transform, and aggregate data. DQL has unique syntax that differs from SQL — load this skill before writing any DQL query.
When to Load References
Before working on specific tasks, load the relevant reference:
| Task |
Required Reading |
| Field names, namespaces, data models, stability levels, query patterns |
references/semantic-dictionary.md |
| Query optimization — make a query faster / more efficient / cheaper, reduce consumption & scanned data (filter early, bucket filters, time ranges, field selection, sampling, cardinality) |
references/optimization.md |
| Smartscape topology navigation for discovering relationships between entities |
references/smartscape-topology-navigation.md |
summarize and makeTimeseries patterns (bucketing, calendar months) |
references/summarization.md |
Array and timeseries manipulation (arrayFilter, collectArray, iterative) |
references/iterative-expressions.md |
Conditional logic (if/else chains), coalesce, string/date helpers |
references/useful-expressions.md |
in operator (subquery), full @ time alignment unit table |
references/operators.md |
DQL Reference Index
Use this index to route from a function group (e.g. time functions, conversions) to its detailed spec, or from a function name to its spec file.
| Description |
Items |
| Data Types |
array, binary, boolean, double, duration, long, record, string, timeframe, timestamp, uid |
| Parameter Value Types |
bucket, dataObject, dplPattern, entityAttribute, entitySelector, entityType, enum, executionBlock, expressionTimeseriesAggregation, expressionWithConstantValue, expressionWithFieldAccess, fieldPattern, filePattern, identifierForAnyField, identifierForEdgeType, identifierForFieldOnRootLevel, identifierForNodeType, joinCondition, jsonPath, metricKey, metricTimeseriesAggregation, namelessDplPattern, nonEmptyExecutionBlock, prefix, primitiveValue, simpleIdentifier, tabularFileExisting, tabularFileNew, url |
| Commands |
append, data, dedup, describe, expand, fetch, fields, fieldsAdd, fieldsFlatten, fieldsKeep, fieldsRemove, fieldsRename, fieldsSnapshot, fieldsSummary, filter, filterOut, join, joinNested, limit, load, lookup, makeTimeseries, metrics, parse, search, smartscapeEdges, smartscapeNodes, sort, summarize, timeseries, traverse |
| Functions — Aggregation |
avg, collectArray, collectDistinct, correlation, count, countDistinct, countDistinctApprox, countDistinctExact, countIf, max, median, min, percentRank, percentile, percentileFromSamples, percentiles, stddev, sum, takeAny, takeFirst, takeLast, takeMax, takeMin, variance |
| Functions — Array |
arrayAvg, arrayConcat, arrayCumulativeSum, arrayDelta, arrayDiff, arrayDistinct, arrayFirst, arrayFlatten, arrayIndexOf, arrayLast, arrayLastIndexOf, arrayMax, arrayMedian, arrayMin, arrayMovingAvg, arrayMovingMax, arrayMovingMin, arrayMovingSum, arrayPercentile, arrayRemoveNulls, arrayReverse, arraySize, arraySlice, arraySort, arraySum, arrayToString, vectorCosineDistance, vectorInnerProductDistance, vectorL1Distance, vectorL2Distance |
| Functions — Bitwise |
bitwiseAnd, bitwiseCountOnes, bitwiseNot, bitwiseOr, bitwiseShiftLeft, bitwiseShiftRight, bitwiseXor |
| Functions — Boolean |
exists, in, isFalseOrNull, isNotNull, isNull, isTrueOrNull, isUid128, isUid64, isUuid |
| Functions — Cast |
asArray, asBinary, asBoolean, asDouble, asDuration, asIp, asLong, asNumber, asRecord, asSmartscapeId, asString, asTimeframe, asTimestamp, asUid |
| Functions — Constant |
e, pi |
| Functions — Conversion |
toArray, toBoolean, toDouble, toDuration, toIp, toLong, toSmartscapeId, toString, toTimeframe, toTimestamp, toUid, toVariant |
| Functions — Create |
array, duration, ip, record, smartscapeId, timeframe, timestamp, timestampFromUnixMillis, timestampFromUnixNanos, timestampFromUnixSeconds, uid128, uid64, uuid |
| Functions — Cryptographic |
hashCrc32, hashMd5, hashSha1, hashSha256, hashSha512, hashXxHash32, hashXxHash64 |
| Functions — Entities |
classicEntitySelector, entityAttr, entityName |
| Functions — Time series aggregation for expressions |
avg, count, countDistinct, countDistinctApprox, countDistinctExact, countIf, end, max, median, min, percentRank, percentile, percentileFromSamples, start, sum |
| Functions — Flow |
coalesce, if |
| Functions — General |
jsonField, jsonPath, lookup, parse, parseAll, type |
| Functions — Get |
arrayElement, getEnd, getHighBits, getLowBits, getStart |
| Functions — Iterative |
iAny, iCollectArray, iIndex |
| Functions — Mathematical |
abs, acos, asin, atan, atan2, bin, cbrt, ceil, cos, cosh, degreeToRadian, exp, floor, hexStringToNumber, hypotenuse, log, log10, log1p, numberToHexString, power, radianToDegree, random, range, round, signum, sin, sinh, sqrt, tan, tanh |
| Functions — Network |
ipIn, ipIsLinkLocal, ipIsLoopback, ipIsPrivate, ipIsPublic, ipMask, isIp, isIpV4, isIpV6 |
| Functions — Smartscape |
getNodeField, getNodeName |
| Functions — String |
concat, contains, decodeBase16ToBinary, decodeBase16ToString, decodeBase64ToBinary, decodeBase64ToString, decodeUrl, encodeBase16, encodeBase64, encodeUrl, endsWith, escape, getCharacter, indexOf, lastIndexOf, levenshteinDistance, like, lower, matchesPattern, matchesPhrase, matchesRegex, matchesValue, punctuation, replacePattern, replaceString, splitByPattern, splitString, startsWith, stringLength, substring, trim, unescape, unescapeHtml, upper |
| Functions — Time |
formatTimestamp, getDayOfMonth, getDayOfWeek, getDayOfYear, getHour, getMinute, getMonth, getSecond, getWeekOfYear, getYear, now, unixMillisFromTimestamp, unixNanosFromTimestamp, unixSecondsFromTimestamp |
| Functions — Time series aggregation for metrics |
avg, count, countDistinct, end, max, median, min, percentRank, percentile, start, sum |
Syntax Pitfalls
| ❌ Wrong |
✅ Right |
Issue |
filter field in ["a", "b"] |
filter in(field, {"a", "b"}) |
[ and ] wrap sub-queries in DQL but do not wrap static array literals. Use {} or array() for static values. |
filter: { in(field, [sub-query]) } (e.g. in timeseries filter:) |
filter: { field in [sub-query] } |
in() does not accept execution blocks as arguments. When the right-hand side is a sub-query (execution block), use the in operator: field in [execution block]. |
by: severity, status |
by: {severity, status} |
List of fields must be grouped by curly braces in by: clauses (summarize, makeTimeseries, etc.). |
contains(toLowercase(field), "err") |
contains(field, "err", false) |
Don't wrap in lower() for case-insensitive matching. contains() has a built-in third positional caseSensitive parameter (default true). |
filter name == "*serv*9*" |
filter matchesValue(name, "*serv*") and matchesValue(name, "*9*") |
== does not support wildcards. matchesValue() supports * wildcards but only at the beginning and/or end of the pattern—split mid-string wildcard intent into multiple calls combined with and. |
matchesValue(field, "prod") on string field |
contains(field, "prod") |
Without wildcards, matchesValue() performs an exact (case-insensitive) match — it will not find "production". Use contains() for substring matching (or matchesValue(field, "*prod*") for wildcard matching). |
toLowercase(field) |
lower(field) |
The function is lower(), not toLowercase(). Only type-casting functions use the to prefix (toString(), toLong(), etc.). |
arrayAvg(field[]) or arraySum(field[]) |
arrayAvg(field) or field[] |
field[] = element-wise iterative expression (array→array); arrayAvg(field) = collapse to scalar (array→single value). Never mix both — arrayAvg(field[]) is semantically wrong. |
my_field after lookup or join |
lookup.my_field / right.my_field |
lookup prefixes added fields with lookup. by default (configurable via prefix:). join prefixes right-side fields with right.. |
substring(field, 0, 200) |
substring(field, from: 0, to: 200) |
The first parameter (expression) is positional, but from: and to: are named optional parameters and must include their names. |
filter host = "A" |
filter host == "A" |
DQL uses == for equality comparison, not =. Single = is assignment (e.g., in fieldsAdd, summarize aliases). |
fetch logs, from: toTimestamp('2026-01-01') |
fetch logs, from: -24h |
from: / to: accept duration literals (e.g., -24h, -7d) or now() expressions — not toTimestamp(). For absolute ranges use timeframe: "start/end" (ISO 8601). |
filter log.level == "ERROR" |
filter loglevel == "ERROR" |
Log severity field is loglevel (no dot) — log.level does not exist. |
sort count() desc |
sort `count()` desc |
Fields with special characters (like parentheses) must be wrapped in backticks. |
length(field) |
stringLength(field) |
DQL string length function is stringLength — there is no length(). |
metrics dt.host.cpu.usage |
timeseries avg(dt.host.cpu.usage) |
metrics loads metric metadata, not values — use timeseries for data. |
join [...], on:{left.a.b == right.a.b} |
join [...], on:{left[`a.b`] == right[`a.b`]} |
Dotted field names in join/lookup conditions require bracket notation with backticks. |
fieldsSummary (no arguments) |
fieldsSummary field1, field2 |
fieldsSummary requires at least one field parameter. |
timeseries with percentile/median/percentRank — no results |
Add rollup: avg (or min/max/sum) to the timeseries command |
These three functions require rollup: on gauge/count metrics — without it the query silently returns empty. |
lookup [...], fields: {`dotted.name`} |
lookup [...], fields: {dotted.name} |
Do not backtick field names inside the fields: parameter of lookup — causes PARSE_ERROR. |
data record(key: "val") |
data record(key = "val") |
record() uses = for named fields, not : — : is for command parameters like rollup:. |
getNodeField(dt.smartscape.host, "tags")["tag.key"] |
getNodeField(dt.smartscape.host, "tags")[tag.key] |
In this tag-map access pattern, bracket keys must use unquoted identifier syntax; quoted keys cause a parse error. |
by: {dt.entity.host} or dt.entity.* |
by: {dt.smartscape.host} or dt.smartscape.* |
dt.entity.* is deprecated — always use dt.smartscape.* in new queries. |
Fetch Command → Data Model
DQL queries start with fetch <data_object> or timeseries. There is no fetch dt.metric — metrics use timeseries.
| Fetch Command |
Data Model |
Key Fields / Notes |
fetch spans |
Distributed tracing |
span.*, service.*, http.*, db.*, code.*, exception.* |
fetch logs |
Log events |
log.*, k8s.*, host.* — message body is content, severity is loglevel (NOT log.level) |
fetch events |
DAVIS / infra events |
event.*, dt.smartscape.* |
fetch bizevents |
Business events |
event.*, custom fields |
fetch security.events |
Security events |
vulnerability.*, event.* |
fetch user.sessions |
RUM sessions |
dt.rum.*, browser.*, geo.* |
fetch user.events |
RUM individual events |
page views, clicks, requests, errors |
fetch user.replays |
Session replay recordings |
|
fetch application.snapshots |
Application snapshots |
|
fetch dt.davis.events |
Davis-detected events |
|
fetch dt.davis.problems |
Davis-detected problems |
|
timeseries avg(metric.key) |
Metrics |
NOT fetch — hyphenated keys need backticks: timeseries sum(`my.metric-name`) |
smartscapeNodes "HOST" |
Topology |
NOT fetch — types: HOST, SERVICE, K8S_CLUSTER, etc. |
dt.entity.* is deprecated — use dt.smartscape.* and smartscapeNodes for new queries.
Discover all available data objects: fetch dt.system.data_objects | fields name, display_name, type
→ references/semantic-dictionary.md for full field namespaces
samplingRatio Parameter
fetch supports a samplingRatio: parameter to reduce the volume of data read — useful for improving query performance on large datasets.
fetch spans, samplingRatio:100 // reads ~1% of data
Allowed values: depend on the concrete data object and range from 1, 10, 100, 1000, 10000 to 100000, the highest level only available for logs and spans.
Sampling is hierarchical for spans, user.events and user.sessions: a record included at a higher ratio (e.g. 100) is guaranteed to also appear at lower ratios (e.g. 10, 1), but not vice versa. This means results at different ratios are subsets of each other. All other non-metric data objects are sampled independently per record, so results at different ratios are not subsets.
The actual ratio applied is accessible via the dt.system.sampling_ratio field. Use it to extrapolate sampled counts back to true totals:
fetch logs, samplingRatio:10
| summarize count_extrapolated = sum(dt.system.sampling_ratio)
Metric Discovery
To search for available metrics by keyword, use the command metrics:
metrics from: now() - 1h
| filter contains(metric.key, "replay")
| summarize count(), by: {metric.key}
| sort `count()` desc
There is no fetch dt.metric or fetch dt.metrics or fetch dt.system.metrics — those data objects do not exist.
Timeseries Aggregation Functions
The timeseries command supports only these aggregation functions:
| Function |
Description |
sum |
Sum of metric data points per time slot |
avg |
Average of metric data points per time slot |
min |
Minimum of metric data points per time slot |
max |
Maximum of metric data points per time slot |
count |
Count of metric data points per time slot |
percentile(metric, N) |
Nth percentile per time slot. Requires rollup: — see below. |
median(metric) |
50th percentile per time slot (= percentile(metric, 50)). Requires rollup:. |
percentRank(metric, value) |
Percentile rank of a value per time slot. Requires rollup:. |
countDistinct(metric) |
Approximate distinct count per time slot (cardinality metrics only; does NOT accept rollup:). |
Helpers (use alongside an aggregation): start(), end().
Not supported by timeseries: countIf, collectArray, stddev, variance, takeAny, takeFirst, takeLast — use summarize or makeTimeseries.
The rollup: parameter
Metrics are pre-aggregated at ingest time. rollup: controls how raw data points are combined per time slot. Required for percentile, median, percentRank — without it the query silently returns no results. avg/min/max/sum/count work without rollup:.
Single aggregation — rollup: at command level. Multiple aggregations in {} — rollup: must go inside each function call (command-level rollup: causes UNKNOWN_PARAMETER_DEFINED):
timeseries p90 = percentile(dt.process.handles.file_descriptors_percent_used, 90), rollup: avg
timeseries {
p90 = percentile(dt.process.handles.file_descriptors_percent_used, 90, rollup: avg),
med = median(dt.process.handles.file_descriptors_percent_used, rollup: avg),
avg_val = avg(dt.process.handles.file_descriptors_percent_used)
}, by: {dt.smartscape.host}
Values: avg (gauges), min, max, sum (counters), total.
Timeseries-to-scalar conversion
There are two ways to collapse a timeseries to a scalar. Prefer the scalar:true parameter when you only need the single aggregated value — it is more efficient because no array is materialized. Fall back to array functions when you need both the full series and a derived scalar in the same query.
Preferred: scalar:true on the aggregation function
Pass scalar:true to any timeseries aggregation function. The result field contains a single value instead of an array, and no intermediate array is allocated:
timeseries avg_cpu = avg(dt.host.cpu.usage, scalar:true), by:{dt.smartscape.host}
timeseries {
avg_cpu = avg(dt.host.cpu.usage, scalar:true),
max_cpu = max(dt.host.cpu.usage, scalar:true)
}, by:{dt.smartscape.host}
Fallback: array functions in fieldsAdd
When you need the full time series array alongside a derived scalar, use array functions in a subsequent | fieldsAdd:
| Function |
Description |
arrayAvg(arr) |
Average of all values in the array |
arraySum(arr) |
Sum of all values |
arrayMin(arr) |
Minimum value |
arrayMax(arr) |
Maximum value |
arrayMedian(arr) |
Median value |
arrayPercentile(arr, N) |
Nth percentile (0–100) |
arrayLast(arr) |
Last non-null value (latest data point) |
arrayFirst(arr) |
First non-null value (earliest data point) |
timeseries cpu = avg(dt.host.cpu.usage), by:{dt.smartscape.host}
| fieldsAdd avg_cpu = arrayAvg(cpu), max_cpu = arrayMax(cpu)
Time Alignment (@-operator)
The @ operator aligns timestamps to a boundary — agents often get this wrong.
| Expression |
Meaning |
now()@h |
Current time, aligned to the hour boundary |
now()@d |
Midnight today |
now()@w1 |
Monday this week |
now()-2h@h |
2 hours ago, aligned to the hour (offset first, then align) |
Rules:
- Order: offset before alignment —
now()-2h@h, not now()@h-2h
- No space between
@ and the unit — now()@h not now() @h
m = minutes, M = months — do not confuse them
→ references/dql/dql-functions-timeseries.md for the full list of timeseries aggregations and rollup: rules
→ references/dql/dql-functions-array.md for arrayAvg / arrayMax / arrayPercentile / … spec
Entity & Smartscape Patterns
Entity fields are scoped per type — entity.id does not exist. Use smartscapeNodes for topology queries.
| Entity |
ID field in data |
smartscapeNodes type |
| Host |
dt.smartscape.host |
"HOST" |
| Service |
dt.smartscape.service |
"SERVICE" |
| Process |
dt.smartscape.process |
"PROCESS" |
| K8s cluster |
dt.smartscape.k8s_cluster |
"K8S_CLUSTER" |
Use toSmartscapeId() for ID conversion from strings (required!).
→ references/smartscape-topology-navigation.md
makeTimeseries Command
makeTimeseries builds a time-bucketed series from event data (logs, spans, bizevents). Unlike timeseries (which queries pre-ingested metrics), makeTimeseries aggregates data in a pipeline.
Do not pipe timeseries directly into makeTimeseries — it fails with INVALID_IMPLICIT_TIME_DEFAULT. To re-aggregate metric data, use start() + expand (see references/summarization.md).
fetch logs
| makeTimeseries
total = count(),
errors = countIf(loglevel == "ERROR"),
interval: 5m,
by: {k8s.cluster.name}
| fieldsAdd error_rate = errors[] * 100.0 / total[]
Key parameters: interval:, by:{}, from:/to:, bins:, time: (timestamp field), spread: (for count/countIf only), nonempty:.
→ references/summarization.md for full makeTimeseries patterns and summarize bucketing
→ references/iterative-expressions.md for timeseries array manipulation
matchesValue() Usage
Use matchesValue() for array fields such as dt.tags:
| filter matchesValue(dt.tags, "env:production")
- Not for string fields with special characters — use
contains() for those
matchesValue() on a scalar string field does not behave like a wildcard or fuzzy match
Chained Lookup Pattern
Each lookup command without a fields parameter removes all existing fields starting with the prefix (default: lookup.) before adding new ones. When chaining multiple lookups, use fields parameter or custom prefixes to preserve the result:
Option 1 (default): the desired fields are known.
fetch bizevents
// Step 1: First lookup — enrich orders with product info
| lookup [fetch bizevents
| filter event.type == "product_catalog"
| fields product_id, category],
sourceField: product_id, lookupField: product_id, fields: {product_id, product_category = category}
// Step 2: Second lookup — specify fields with a different name
| lookup [fetch bizevents
| filter event.type == "warehouse_stock"
| fields category, warehouse_region],
sourceField: product_category, lookupField: category, fields: {warehouse_region, warehouse_category = category}
All 4 lookup fields product_id, product_category, warehouse_region, and warehouse_category are available.
Without the fields:{...} parameter, the fields would be prefixed with lookup. and the second lookup command would delete the fields added by the first lookup.
Option 2: keep all fields from the lookup.
fetch bizevents
// Step 1: First lookup — enrich orders with product info
| lookup [fetch bizevents
| filter event.type == "product_catalog"
| fields product_id, category],
sourceField: product_id, lookupField: product_id, prefix: "product."
// Step 2: Second lookup — specify fields with a different prefix
| lookup [fetch bizevents
| filter event.type == "warehouse_stock"
| fields category, warehouse_region],
sourceField: product_category, lookupField: category, prefix: "warehouse."
The new fields are: product.product_id, product.category, warehouse.category, warehouse.warehouse_region.
All fields starting with product. or warehouse. are removed from the original source.
Without the dedicated prefix, both lookup commands would use the same prefix (lookup.) and the second lookup drops the first lookup's results — producing empty fields.
makeTimeseries Command
makeTimeseries builds a time-bucketed series from event data (logs, spans, bizevents). Unlike timeseries (which queries pre-ingested metrics), makeTimeseries aggregates data in a pipeline.
Do not pipe timeseries directly into makeTimeseries — it fails with INVALID_IMPLICIT_TIME_DEFAULT. To re-aggregate metric data, use start() + expand (see references/summarization.md).
fetch logs
| makeTimeseries
{total = count(),
errors = countIf(loglevel == "ERROR")},
interval: 5m,
by: {k8s.cluster.name}
| fieldsAdd error_rate = errors[] * 100.0 / total[]
Key parameters: interval:, by:{}, from:/to:, bins:, time: (timestamp field), spread: (for count/countIf only), nonempty:. → references/dql/dql-commands.md for full spec.
Entity existence timeline using spread::
smartscapeNodes "HOST"
| makeTimeseries concurrently_existing_hosts = count(), spread: lifetime
→ references/iterative-expressions.md for timeseries array manipulation
Timeframe Specification
Access to data requires specification of a timeframe.
It can be specified in the UI, as REST API parameters, or in a DQL query explicitly using a pair of parameters: from: and to: (if one is omitted it defaults to now()), or alternatively using a single timeframe: parameter.
Timeframe can be expressed using absolute values or relative expressions vs. current time. The time alignment operator (@) can be used to round timestamps to time unit boundaries — see references/operators.md for full details.
Examples
from:now()-1h@h, to:now()@h // last complete hour
from:now()-1d@d, to:now()@d // yesterday complete
from:now()@M // this month so far, till now
from:now()-2h@h // go back 2 hours, then align to hour boundary
See references/operators.md for the full @ alignment-unit table (including m vs. M, week-day variants w1–w7, and factor rules like @3h).
Absolute timestamps
Use ISO 8601 format:
from:"2024-01-15T08:00:00Z", to:"2024-01-15T09:00:00Z"
Modifying Time
Key concepts
- DQL has 3 specialized types related to time:
- timestamp — internally kept as number of nanoseconds since epoch, but exposed as date/time in a particular timezone
- timeframe — a pair of 2 timestamps (start and end)
- duration — internally kept as number of nanoseconds, but exposed as duration scaled to a reasonable factor (e.g. ms, minutes, days)
Rules
- Subtracting timestamps yields a duration:
timestamp - timestamp → duration
- Duration divided by duration yields a double: e.g.
2h / 1m = 120.0
- Scalar times duration yields a duration: e.g.
no_of_h * 1h → duration
- For extraction of time elements (hours, days of month, etc):
- ✅ Use time functions. They support calendar and time zones properly including DST.
- ❌ Avoid using
formatTimestamp for extracting time components.
- ❌ Avoid converting timestamps and durations to double/long and using division, modulo, and constants expressing time units as nanoseconds.
References
- references/useful-expressions.md — Useful expressions in DQL
- references/semantic-dictionary.md — Dynatrace Semantic Dictionary: field namespaces, data models, stability levels, query patterns, and best practices
- references/summarization.md — Various applications of summarize and makeTimeseries commands
- references/iterative-expressions.md — Array and timeseries manipulation (creation, modifications, use in filters) using DQL
- references/smartscape-topology-navigation.md — Smartscape topology navigation syntax and patterns
- references/optimization.md — DQL query optimization: making queries faster, more efficient, and cheaper to run (lower consumption / scanned data per execution) — filter placement, bucket filters, time ranges, field selection, sampling, cardinality, and performance best practices
- references/operators.md —
in operator (subquery syntax) and full @ time alignment unit reference
1---2name: dt-dql-essentials3description: Core DQL syntax, pitfalls, query patterns, and query optimization. Load to write, build, fix, or OPTIMIZE a DQL query — prevents syntax errors and makes queries faster, more efficient, and cheaper (less data scanned = lower query consumption/cost per run). Covers fetch commands, data models, field namespaces, time alignment, entity/smartscape patterns, metric discovery, and performance/cost optimization (filter early, bucket filters, short time ranges, field selection, sampling, cardinality). Trigger: "write/build/fix a DQL query", "DQL syntax", "query logs/spans/metrics", "create a timeseries", "optimize my DQL", "make my query faster/cheaper", "reduce DQL cost/consumption/scanned data", "keep DQL cost under control". Do NOT use to explain an existing query or answer product questions. For MONITORING a tenant's ACTUAL query consumption/billing (how much queries cost, who scanned most, cost trends) use dt-platform-costs — this tunes the query text, not billing data.4license: Apache-2.05---67# DQL Essentials Skill89DQL is a pipeline-based query language. Queries chain commands with `|` to filter, transform, and aggregate data. DQL has unique syntax that differs from SQL — load this skill before writing any DQL query.1011______________________________________________________________________1213## When to Load References1415Before working on specific tasks, load the relevant reference:1617| Task | Required Reading |18| ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |19| Field names, namespaces, data models, stability levels, query patterns | [references/semantic-dictionary.md](references/semantic-dictionary.md) |20| Query optimization — make a query faster / more efficient / cheaper, reduce consumption & scanned data (filter early, bucket filters, time ranges, field selection, sampling, cardinality) | [references/optimization.md](references/optimization.md) |21| Smartscape topology navigation for discovering relationships between entities | [references/smartscape-topology-navigation.md](references/smartscape-topology-navigation.md) |22| `summarize` and `makeTimeseries` patterns (bucketing, calendar months) | [references/summarization.md](references/summarization.md) |23| Array and timeseries manipulation (`arrayFilter`, `collectArray`, iterative) | [references/iterative-expressions.md](references/iterative-expressions.md) |24| Conditional logic (`if/else` chains), `coalesce`, string/date helpers | [references/useful-expressions.md](references/useful-expressions.md) |25| `in` operator (subquery), full `@` time alignment unit table | [references/operators.md](references/operators.md) |2627______________________________________________________________________2829## DQL Reference Index3031Use this index to route from a function group (e.g. time functions, conversions) to its detailed spec, or from a function name to its spec file.3233| Description | Items |34|-------------|-------|35| [Data Types](references/dql/dql-data-types.md) | `array`, `binary`, `boolean`, `double`, `duration`, `long`, `record`, `string`, `timeframe`, `timestamp`, `uid` |36| [Parameter Value Types](references/dql/dql-parameter-value-types.md) | `bucket`, `dataObject`, `dplPattern`, `entityAttribute`, `entitySelector`, `entityType`, `enum`, `executionBlock`, `expressionTimeseriesAggregation`, `expressionWithConstantValue`, `expressionWithFieldAccess`, `fieldPattern`, `filePattern`, `identifierForAnyField`, `identifierForEdgeType`, `identifierForFieldOnRootLevel`, `identifierForNodeType`, `joinCondition`, `jsonPath`, `metricKey`, `metricTimeseriesAggregation`, `namelessDplPattern`, `nonEmptyExecutionBlock`, `prefix`, `primitiveValue`, `simpleIdentifier`, `tabularFileExisting`, `tabularFileNew`, `url` |37| [Commands](references/dql/dql-commands.md) | `append`, `data`, `dedup`, `describe`, `expand`, `fetch`, `fields`, `fieldsAdd`, `fieldsFlatten`, `fieldsKeep`, `fieldsRemove`, `fieldsRename`, `fieldsSnapshot`, `fieldsSummary`, `filter`, `filterOut`, `join`, `joinNested`, `limit`, `load`, `lookup`, `makeTimeseries`, `metrics`, `parse`, `search`, `smartscapeEdges`, `smartscapeNodes`, `sort`, `summarize`, `timeseries`, `traverse` |38| [Functions — Aggregation](references/dql/dql-functions-aggregation.md) | `avg`, `collectArray`, `collectDistinct`, `correlation`, `count`, `countDistinct`, `countDistinctApprox`, `countDistinctExact`, `countIf`, `max`, `median`, `min`, `percentRank`, `percentile`, `percentileFromSamples`, `percentiles`, `stddev`, `sum`, `takeAny`, `takeFirst`, `takeLast`, `takeMax`, `takeMin`, `variance` |39| [Functions — Array](references/dql/dql-functions-array.md) | `arrayAvg`, `arrayConcat`, `arrayCumulativeSum`, `arrayDelta`, `arrayDiff`, `arrayDistinct`, `arrayFirst`, `arrayFlatten`, `arrayIndexOf`, `arrayLast`, `arrayLastIndexOf`, `arrayMax`, `arrayMedian`, `arrayMin`, `arrayMovingAvg`, `arrayMovingMax`, `arrayMovingMin`, `arrayMovingSum`, `arrayPercentile`, `arrayRemoveNulls`, `arrayReverse`, `arraySize`, `arraySlice`, `arraySort`, `arraySum`, `arrayToString`, `vectorCosineDistance`, `vectorInnerProductDistance`, `vectorL1Distance`, `vectorL2Distance` |40| [Functions — Bitwise](references/dql/dql-functions-bitwise.md) | `bitwiseAnd`, `bitwiseCountOnes`, `bitwiseNot`, `bitwiseOr`, `bitwiseShiftLeft`, `bitwiseShiftRight`, `bitwiseXor` |41| [Functions — Boolean](references/dql/dql-functions-boolean.md) | `exists`, `in`, `isFalseOrNull`, `isNotNull`, `isNull`, `isTrueOrNull`, `isUid128`, `isUid64`, `isUuid` |42| [Functions — Cast](references/dql/dql-functions-cast.md) | `asArray`, `asBinary`, `asBoolean`, `asDouble`, `asDuration`, `asIp`, `asLong`, `asNumber`, `asRecord`, `asSmartscapeId`, `asString`, `asTimeframe`, `asTimestamp`, `asUid` |43| [Functions — Constant](references/dql/dql-functions-constant.md) | `e`, `pi` |44| [Functions — Conversion](references/dql/dql-functions-conversion.md) | `toArray`, `toBoolean`, `toDouble`, `toDuration`, `toIp`, `toLong`, `toSmartscapeId`, `toString`, `toTimeframe`, `toTimestamp`, `toUid`, `toVariant` |45| [Functions — Create](references/dql/dql-functions-create.md) | `array`, `duration`, `ip`, `record`, `smartscapeId`, `timeframe`, `timestamp`, `timestampFromUnixMillis`, `timestampFromUnixNanos`, `timestampFromUnixSeconds`, `uid128`, `uid64`, `uuid` |46| [Functions — Cryptographic](references/dql/dql-functions-cryptographic.md) | `hashCrc32`, `hashMd5`, `hashSha1`, `hashSha256`, `hashSha512`, `hashXxHash32`, `hashXxHash64` |47| [Functions — Entities](references/dql/dql-functions-entities.md) | `classicEntitySelector`, `entityAttr`, `entityName` |48| [Functions — Time series aggregation for expressions](references/dql/dql-functions-expression-timeseries.md) | `avg`, `count`, `countDistinct`, `countDistinctApprox`, `countDistinctExact`, `countIf`, `end`, `max`, `median`, `min`, `percentRank`, `percentile`, `percentileFromSamples`, `start`, `sum` |49| [Functions — Flow](references/dql/dql-functions-flow.md) | `coalesce`, `if` |50| [Functions — General](references/dql/dql-functions-general.md) | `jsonField`, `jsonPath`, `lookup`, `parse`, `parseAll`, `type` |51| [Functions — Get](references/dql/dql-functions-get.md) | `arrayElement`, `getEnd`, `getHighBits`, `getLowBits`, `getStart` |52| [Functions — Iterative](references/dql/dql-functions-iterative.md) | `iAny`, `iCollectArray`, `iIndex` |53| [Functions — Mathematical](references/dql/dql-functions-mathematical.md) | `abs`, `acos`, `asin`, `atan`, `atan2`, `bin`, `cbrt`, `ceil`, `cos`, `cosh`, `degreeToRadian`, `exp`, `floor`, `hexStringToNumber`, `hypotenuse`, `log`, `log10`, `log1p`, `numberToHexString`, `power`, `radianToDegree`, `random`, `range`, `round`, `signum`, `sin`, `sinh`, `sqrt`, `tan`, `tanh` |54| [Functions — Network](references/dql/dql-functions-network.md) | `ipIn`, `ipIsLinkLocal`, `ipIsLoopback`, `ipIsPrivate`, `ipIsPublic`, `ipMask`, `isIp`, `isIpV4`, `isIpV6` |55| [Functions — Smartscape](references/dql/dql-functions-smartscape.md) | `getNodeField`, `getNodeName` |56| [Functions — String](references/dql/dql-functions-string.md) | `concat`, `contains`, `decodeBase16ToBinary`, `decodeBase16ToString`, `decodeBase64ToBinary`, `decodeBase64ToString`, `decodeUrl`, `encodeBase16`, `encodeBase64`, `encodeUrl`, `endsWith`, `escape`, `getCharacter`, `indexOf`, `lastIndexOf`, `levenshteinDistance`, `like`, `lower`, `matchesPattern`, `matchesPhrase`, `matchesRegex`, `matchesValue`, `punctuation`, `replacePattern`, `replaceString`, `splitByPattern`, `splitString`, `startsWith`, `stringLength`, `substring`, `trim`, `unescape`, `unescapeHtml`, `upper` |57| [Functions — Time](references/dql/dql-functions-time.md) | `formatTimestamp`, `getDayOfMonth`, `getDayOfWeek`, `getDayOfYear`, `getHour`, `getMinute`, `getMonth`, `getSecond`, `getWeekOfYear`, `getYear`, `now`, `unixMillisFromTimestamp`, `unixNanosFromTimestamp`, `unixSecondsFromTimestamp` |58| [Functions — Time series aggregation for metrics](references/dql/dql-functions-timeseries.md) | `avg`, `count`, `countDistinct`, `end`, `max`, `median`, `min`, `percentRank`, `percentile`, `start`, `sum` |5960______________________________________________________________________6162## Syntax Pitfalls6364| ❌ Wrong | ✅ Right | Issue |65| --- | --- | --- |66| `filter field in ["a", "b"]` | `filter in(field, {"a", "b"})` | `[` and `]` wrap sub-queries in DQL but do not wrap **static** array literals. Use `{}` or `array()` for static values. |67| `filter: { in(field, [sub-query]) }` (e.g. in `timeseries filter:`) | `filter: { field in [sub-query] }` | `in()` does not accept execution blocks as arguments. When the right-hand side is a sub-query (execution block), use the `in` operator: `field in [execution block]`. |68| `by: severity, status` | `by: {severity, status}` | List of fields must be grouped by curly braces in `by:` clauses (`summarize`, `makeTimeseries`, etc.). |69| `contains(toLowercase(field), "err")` | `contains(field, "err", false)` | Don't wrap in `lower()` for case-insensitive matching. `contains()` has a built-in third positional `caseSensitive` parameter (default `true`). |70| `filter name == "*serv*9*"` | `filter matchesValue(name, "*serv*") and matchesValue(name, "*9*")` | `==` does not support wildcards. `matchesValue()` supports `*` wildcards but only at the beginning and/or end of the pattern—split mid-string wildcard intent into multiple calls combined with `and`. |71| `matchesValue(field, "prod")` on string field | `contains(field, "prod")` | Without wildcards, `matchesValue()` performs an exact (case-insensitive) match — it will not find `"production"`. Use `contains()` for substring matching (or `matchesValue(field, "*prod*")` for wildcard matching). |72| `toLowercase(field)` | `lower(field)` | The function is `lower()`, not `toLowercase()`. Only type-casting functions use the `to` prefix (`toString()`, `toLong()`, etc.). |73| `arrayAvg(field[])` or `arraySum(field[])` | `arrayAvg(field)` or `field[]` | `field[]` = element-wise iterative expression (array→array); `arrayAvg(field)` = collapse to scalar (array→single value). Never mix both — `arrayAvg(field[])` is semantically wrong. |74| `my_field` after `lookup` or `join` | `lookup.my_field` / `right.my_field` | `lookup` prefixes added fields with `lookup.` by default (configurable via `prefix:`). `join` prefixes right-side fields with `right.`. |75| `substring(field, 0, 200)` | `substring(field, from: 0, to: 200)` | The first parameter (expression) is positional, but `from:` and `to:` are named optional parameters and must include their names. |76| `filter host = "A"` | `filter host == "A"` | DQL uses `==` for equality comparison, not `=`. Single `=` is assignment (e.g., in `fieldsAdd`, summarize aliases). |77| `fetch logs, from: toTimestamp('2026-01-01')` | `fetch logs, from: -24h` | `from:` / `to:` accept duration literals (e.g., `-24h`, `-7d`) or `now()` expressions — not `toTimestamp()`. For absolute ranges use `timeframe: "start/end"` (ISO 8601). |78| `filter log.level == "ERROR"` | `filter loglevel == "ERROR"` | Log severity field is `loglevel` (no dot) — `log.level` does not exist. |79| `sort count() desc` | `` sort `count()` desc `` | Fields with special characters (like parentheses) must be wrapped in backticks. |80| `length(field)` | `stringLength(field)` | DQL string length function is `stringLength` — there is no `length()`. |81| `metrics dt.host.cpu.usage` | `timeseries avg(dt.host.cpu.usage)` | `metrics` loads metric metadata, not values — use `timeseries` for data. |82| `join [...], on:{left.a.b == right.a.b}` | `` join [...], on:{left[`a.b`] == right[`a.b`]} `` | Dotted field names in join/lookup conditions require bracket notation with backticks. |83| `fieldsSummary` (no arguments) | `fieldsSummary field1, field2` | `fieldsSummary` requires at least one field parameter. |84| `timeseries` with `percentile`/`median`/`percentRank` — no results | Add `rollup: avg` (or `min`/`max`/`sum`) to the `timeseries` command | These three functions **require `rollup:`** on gauge/count metrics — without it the query silently returns empty. |85| `` lookup [...], fields: {`dotted.name`} `` | `lookup [...], fields: {dotted.name}` | Do not backtick field names inside the `fields:` parameter of `lookup` — causes PARSE_ERROR. |86| `data record(key: "val")` | `data record(key = "val")` | `record()` uses `=` for named fields, not `:` — `:` is for command parameters like `rollup:`. |87| `getNodeField(dt.smartscape.host, "tags")["tag.key"]` | `getNodeField(dt.smartscape.host, "tags")[tag.key]` | In this tag-map access pattern, bracket keys must use unquoted identifier syntax; quoted keys cause a parse error. |88| `by: {dt.entity.host}` or `dt.entity.*` | `by: {dt.smartscape.host}` or `dt.smartscape.*` | `dt.entity.*` is **deprecated** — always use `dt.smartscape.*` in new queries. |8990______________________________________________________________________9192## Fetch Command → Data Model9394DQL queries start with `fetch <data_object>` or `timeseries`. There is **no `fetch dt.metric`** — metrics use `timeseries`.9596| Fetch Command | Data Model | Key Fields / Notes |97|---------------|------------|--------------------|98| `fetch spans` | Distributed tracing | `span.*`, `service.*`, `http.*`, `db.*`, `code.*`, `exception.*` |99| `fetch logs` | Log events | `log.*`, `k8s.*`, `host.*` — message body is `content`, severity is `loglevel` (NOT `log.level`) |100| `fetch events` | DAVIS / infra events | `event.*`, `dt.smartscape.*` |101| `fetch bizevents` | Business events | `event.*`, custom fields |102| `fetch security.events` | Security events | `vulnerability.*`, `event.*` |103| `fetch user.sessions` | RUM sessions | `dt.rum.*`, `browser.*`, `geo.*` |104| `fetch user.events` | RUM individual events | page views, clicks, requests, errors |105| `fetch user.replays` | Session replay recordings | |106| `fetch application.snapshots` | Application snapshots | |107| `fetch dt.davis.events` | Davis-detected events | |108| `fetch dt.davis.problems` | Davis-detected problems | |109| `timeseries avg(metric.key)` | Metrics | NOT `fetch` — hyphenated keys need backticks: `` timeseries sum(`my.metric-name`) `` |110| `smartscapeNodes "HOST"` | Topology | NOT `fetch` — types: `HOST`, `SERVICE`, `K8S_CLUSTER`, etc. |111112`dt.entity.*` is deprecated — use `dt.smartscape.*` and `smartscapeNodes` for new queries.113114Discover all available data objects: `fetch dt.system.data_objects | fields name, display_name, type`115116→ [references/semantic-dictionary.md](references/semantic-dictionary.md) for full field namespaces117118______________________________________________________________________119120## `samplingRatio` Parameter121122`fetch` supports a `samplingRatio:` parameter to reduce the volume of data read — useful for improving query performance on large datasets.123124```dql125fetch spans, samplingRatio:100 // reads ~1% of data126```127128**Allowed values:** depend on the concrete data object and range from `1`, `10`, `100`, `1000`, `10000` to `100000`, the highest level only available for `logs` and `spans`.129130131Sampling is **hierarchical** for `spans`, `user.events` and `user.sessions`: a record included at a higher ratio (e.g. `100`) is guaranteed to also appear at lower ratios (e.g. `10`, `1`), but not vice versa. This means results at different ratios are subsets of each other. All other non-metric data objects are sampled independently per record, so results at different ratios are not subsets.132133The actual ratio applied is accessible via the `dt.system.sampling_ratio` field. Use it to extrapolate sampled counts back to true totals:134135```dql136fetch logs, samplingRatio:10137| summarize count_extrapolated = sum(dt.system.sampling_ratio)138```139140______________________________________________________________________141142## Metric Discovery143144To search for available metrics by keyword, use the command `metrics`:145146```dql147metrics from: now() - 1h148| filter contains(metric.key, "replay")149| summarize count(), by: {metric.key}150| sort `count()` desc151```152153There is **no `fetch dt.metric`** or `fetch dt.metrics` or `fetch dt.system.metrics` — those data objects do not exist.154155______________________________________________________________________156157## Timeseries Aggregation Functions158159The `timeseries` command supports only these aggregation functions:160161| Function | Description |162|----------|-------------|163| `sum` | Sum of metric data points per time slot |164| `avg` | Average of metric data points per time slot |165| `min` | Minimum of metric data points per time slot |166| `max` | Maximum of metric data points per time slot |167| `count` | Count of metric data points per time slot |168| `percentile(metric, N)` | Nth percentile per time slot. **Requires `rollup:`** — see below. |169| `median(metric)` | 50th percentile per time slot (= `percentile(metric, 50)`). **Requires `rollup:`**. |170| `percentRank(metric, value)` | Percentile rank of a value per time slot. **Requires `rollup:`**. |171| `countDistinct(metric)` | Approximate distinct count per time slot (cardinality metrics only; does NOT accept `rollup:`). |172173Helpers (use alongside an aggregation): `start()`, `end()`.174175**Not supported by `timeseries`:** `countIf`, `collectArray`, `stddev`, `variance`, `takeAny`, `takeFirst`, `takeLast` — use `summarize` or `makeTimeseries`.176177### The `rollup:` parameter178179Metrics are pre-aggregated at ingest time. `rollup:` controls how raw data points are combined per time slot. Required for `percentile`, `median`, `percentRank` — without it the query silently returns no results. `avg`/`min`/`max`/`sum`/`count` work without `rollup:`.180181Single aggregation — `rollup:` at command level. Multiple aggregations in `{}` — `rollup:` must go **inside each function call** (command-level `rollup:` causes `UNKNOWN_PARAMETER_DEFINED`):182183```dql184timeseries p90 = percentile(dt.process.handles.file_descriptors_percent_used, 90), rollup: avg185```186187```dql188timeseries {189 p90 = percentile(dt.process.handles.file_descriptors_percent_used, 90, rollup: avg),190 med = median(dt.process.handles.file_descriptors_percent_used, rollup: avg),191 avg_val = avg(dt.process.handles.file_descriptors_percent_used)192}, by: {dt.smartscape.host}193```194195Values: `avg` (gauges), `min`, `max`, `sum` (counters), `total`.196197### Timeseries-to-scalar conversion198199There are two ways to collapse a timeseries to a scalar. Prefer the `scalar:true` parameter when you only need the single aggregated value — it is more efficient because no array is materialized. Fall back to array functions when you need both the full series and a derived scalar in the same query.200201**Preferred: `scalar:true` on the aggregation function**202203Pass `scalar:true` to any timeseries aggregation function. The result field contains a single value instead of an array, and no intermediate array is allocated:204205```dql206timeseries avg_cpu = avg(dt.host.cpu.usage, scalar:true), by:{dt.smartscape.host}207```208209```dql210timeseries {211 avg_cpu = avg(dt.host.cpu.usage, scalar:true),212 max_cpu = max(dt.host.cpu.usage, scalar:true)213}, by:{dt.smartscape.host}214```215216**Fallback: array functions in `fieldsAdd`**217218When you need the full time series array alongside a derived scalar, use array functions in a subsequent `| fieldsAdd`:219220| Function | Description |221|----------|-------------|222| `arrayAvg(arr)` | Average of all values in the array |223| `arraySum(arr)` | Sum of all values |224| `arrayMin(arr)` | Minimum value |225| `arrayMax(arr)` | Maximum value |226| `arrayMedian(arr)` | Median value |227| `arrayPercentile(arr, N)` | Nth percentile (0–100) |228| `arrayLast(arr)` | Last non-null value (latest data point) |229| `arrayFirst(arr)` | First non-null value (earliest data point) |230231```dql232timeseries cpu = avg(dt.host.cpu.usage), by:{dt.smartscape.host}233| fieldsAdd avg_cpu = arrayAvg(cpu), max_cpu = arrayMax(cpu)234```235236______________________________________________________________________237238## Time Alignment (@-operator)239240The `@` operator aligns timestamps to a boundary — agents often get this wrong.241242| Expression | Meaning |243| ------------ | ----------------------------------------------------------- |244| `now()@h` | Current time, aligned to the hour boundary |245| `now()@d` | Midnight today |246| `now()@w1` | Monday this week |247| `now()-2h@h` | 2 hours ago, aligned to the hour (offset first, then align) |248249**Rules:**250251- Order: offset before alignment — `now()-2h@h`, not `now()@h-2h`252- No space between `@` and the unit — `now()@h` not `now() @h`253- `m` = minutes, `M` = months — do not confuse them254255→ [references/dql/dql-functions-timeseries.md](references/dql/dql-functions-timeseries.md) for the full list of `timeseries` aggregations and `rollup:` rules256→ [references/dql/dql-functions-array.md](references/dql/dql-functions-array.md) for `arrayAvg` / `arrayMax` / `arrayPercentile` / … spec257258______________________________________________________________________259260## Entity & Smartscape Patterns261262Entity fields are scoped per type — `entity.id` does not exist. Use `smartscapeNodes` for topology queries.263264| Entity | ID field in data | `smartscapeNodes` type |265| ----------- | ---------------------------- | ---------------------- |266| Host | `dt.smartscape.host` | `"HOST"` |267| Service | `dt.smartscape.service` | `"SERVICE"` |268| Process | `dt.smartscape.process` | `"PROCESS"` |269| K8s cluster | `dt.smartscape.k8s_cluster` | `"K8S_CLUSTER"` |270271Use `toSmartscapeId()` for ID conversion from strings (required!).272273→ [references/smartscape-topology-navigation.md](references/smartscape-topology-navigation.md)274275______________________________________________________________________276277## makeTimeseries Command278279`makeTimeseries` builds a time-bucketed series from event data (logs, spans, bizevents). Unlike `timeseries` (which queries pre-ingested metrics), `makeTimeseries` aggregates data in a pipeline.280281**Do not pipe `timeseries` directly into `makeTimeseries`** — it fails with `INVALID_IMPLICIT_TIME_DEFAULT`. To re-aggregate metric data, use `start()` + expand (see [references/summarization.md](references/summarization.md)).282283```dql284fetch logs285| makeTimeseries286 total = count(),287 errors = countIf(loglevel == "ERROR"),288 interval: 5m,289 by: {k8s.cluster.name}290| fieldsAdd error_rate = errors[] * 100.0 / total[]291```292293Key parameters: `interval:`, `by:{}`, `from:`/`to:`, `bins:`, `time:` (timestamp field), `spread:` (for `count`/`countIf` only), `nonempty:`.294295→ [references/summarization.md](references/summarization.md) for full `makeTimeseries` patterns and `summarize` bucketing296→ [references/iterative-expressions.md](references/iterative-expressions.md) for timeseries array manipulation297298______________________________________________________________________299300## matchesValue() Usage301302Use `matchesValue()` for **array fields** such as `dt.tags`:303304```dql-snippet305| filter matchesValue(dt.tags, "env:production")306```307308- **Not** for string fields with special characters — use `contains()` for those309- `matchesValue()` on a scalar string field does not behave like a wildcard or fuzzy match310311______________________________________________________________________312313## Chained Lookup Pattern314315316Each `lookup` command without a `fields` parameter **removes all existing fields starting with the prefix (default: `lookup.`)** before adding new ones. When chaining multiple lookups, use `fields` parameter or custom prefixes to preserve the result:317318**Option 1 (default)**: the desired fields are known.319```dql320fetch bizevents321// Step 1: First lookup — enrich orders with product info322| lookup [fetch bizevents323 | filter event.type == "product_catalog"324 | fields product_id, category],325 sourceField: product_id, lookupField: product_id, fields: {product_id, product_category = category}326327// Step 2: Second lookup — specify fields with a different name328| lookup [fetch bizevents329 | filter event.type == "warehouse_stock"330 | fields category, warehouse_region],331 sourceField: product_category, lookupField: category, fields: {warehouse_region, warehouse_category = category}332333```334All 4 lookup fields product_id, product_category, warehouse_region, and warehouse_category are available.335Without the `fields:{...}` parameter, the fields would be prefixed with `lookup.` and the second lookup command would delete the fields added by the first lookup.336337**Option 2**: keep all fields from the lookup.338```dql339fetch bizevents340// Step 1: First lookup — enrich orders with product info341| lookup [fetch bizevents342 | filter event.type == "product_catalog"343 | fields product_id, category],344 sourceField: product_id, lookupField: product_id, prefix: "product."345346// Step 2: Second lookup — specify fields with a different prefix347| lookup [fetch bizevents348 | filter event.type == "warehouse_stock"349 | fields category, warehouse_region],350 sourceField: product_category, lookupField: category, prefix: "warehouse."351352```353The new fields are: `product.product_id`, `product.category`, `warehouse.category`, `warehouse.warehouse_region`.354All fields starting with `product.` or `warehouse.` are removed from the original source.355Without the dedicated `prefix`, both `lookup` commands would use the same prefix (`lookup.`) and the second `lookup` drops the first lookup's results — producing empty fields.356357______________________________________________________________________358359## makeTimeseries Command360361`makeTimeseries` builds a time-bucketed series from event data (logs, spans, bizevents). Unlike `timeseries` (which queries pre-ingested metrics), `makeTimeseries` aggregates data in a pipeline.362363**Do not pipe `timeseries` directly into `makeTimeseries`** — it fails with `INVALID_IMPLICIT_TIME_DEFAULT`. To re-aggregate metric data, use `start()` + expand (see [references/summarization.md](references/summarization.md)).364365```dql366fetch logs367| makeTimeseries368 {total = count(),369 errors = countIf(loglevel == "ERROR")},370 interval: 5m,371 by: {k8s.cluster.name}372| fieldsAdd error_rate = errors[] * 100.0 / total[]373```374375Key parameters: `interval:`, `by:{}`, `from:`/`to:`, `bins:`, `time:` (timestamp field), `spread:` (for `count`/`countIf` only), `nonempty:`. → [references/dql/dql-commands.md](references/dql/dql-commands.md) for full spec.376377Entity existence timeline using `spread:`:378379```dql380smartscapeNodes "HOST"381| makeTimeseries concurrently_existing_hosts = count(), spread: lifetime382```383384→ [references/iterative-expressions.md](references/iterative-expressions.md) for timeseries array manipulation385386______________________________________________________________________387388## Timeframe Specification389390Access to data requires specification of a timeframe.391It can be specified in the UI, as REST API parameters, or in a DQL query explicitly using a pair of parameters: `from:` and `to:` (if one is omitted it defaults to `now()`), or alternatively using a single `timeframe:` parameter.392Timeframe can be expressed using absolute values or relative expressions vs. current time. The time alignment operator (`@`) can be used to round timestamps to time unit boundaries — see [references/operators.md](references/operators.md) for full details.393394### Examples395396```dql-snippet397from:now()-1h@h, to:now()@h // last complete hour398```399```dql-snippet400from:now()-1d@d, to:now()@d // yesterday complete401```402```dql-snippet403from:now()@M // this month so far, till now404```405```dql-snippet406from:now()-2h@h // go back 2 hours, then align to hour boundary407```408409See [references/operators.md](references/operators.md) for the full `@` alignment-unit table (including `m` vs. `M`, week-day variants `w1`–`w7`, and factor rules like `@3h`).410411### Absolute timestamps412413Use ISO 8601 format:414415```dql-snippet416from:"2024-01-15T08:00:00Z", to:"2024-01-15T09:00:00Z"417```418419______________________________________________________________________420421## Modifying Time422423### Key concepts424425- DQL has 3 specialized types related to time:426 - **timestamp** — internally kept as number of nanoseconds since epoch, but exposed as date/time in a particular timezone427 - **timeframe** — a pair of 2 timestamps (start and end)428 - **duration** — internally kept as number of nanoseconds, but exposed as duration scaled to a reasonable factor (e.g. ms, minutes, days)429430### Rules431432- Subtracting timestamps yields a duration: `timestamp - timestamp → duration`433- Duration divided by duration yields a double: e.g. `2h / 1m` = `120.0`434- Scalar times duration yields a duration: e.g. `no_of_h * 1h → duration`435- For extraction of time elements (hours, days of month, etc):436 - ✅ Use [time functions](references/dql/dql-functions-time.md). They support calendar and time zones properly including DST.437 - ❌ Avoid using `formatTimestamp` for extracting time components.438 - ❌ Avoid converting timestamps and durations to double/long and using division, modulo, and constants expressing time units as nanoseconds.439440## References441442- **[references/useful-expressions.md](references/useful-expressions.md)** — Useful expressions in DQL443- **[references/semantic-dictionary.md](references/semantic-dictionary.md)** — Dynatrace Semantic Dictionary: field namespaces, data models, stability levels, query patterns, and best practices444- **[references/summarization.md](references/summarization.md)** — Various applications of summarize and makeTimeseries commands445- **[references/iterative-expressions.md](references/iterative-expressions.md)** — Array and timeseries manipulation (creation, modifications, use in filters) using DQL446- **[references/smartscape-topology-navigation.md](references/smartscape-topology-navigation.md)** — Smartscape topology navigation syntax and patterns447- **[references/optimization.md](references/optimization.md)** — DQL query optimization: making queries faster, more efficient, and cheaper to run (lower consumption / scanned data per execution) — filter placement, bucket filters, time ranges, field selection, sampling, cardinality, and performance best practices448- **[references/operators.md](references/operators.md)** — `in` operator (subquery syntax) and full `@` time alignment unit reference