Debug Query Syntax
Understand the placeholders in a Validio debug query and how they let a saved query be reused across incidents.
The debug query that Validio generates when you debug an incident contains placeholders rather than values specific to that one incident. A placeholder is written as {{ validio_... }}, and Validio replaces it with the incident's own values at the moment the query runs.
This is what makes a debug query reusable. Because the time range and segment are placeholders rather than fixed values, the same query can be saved once and run against a different incident later, where it investigates that incident's data instead.
Anatomy of a Debug Query
The generated query is a SELECT against the source table with a WHERE clause assembled from several labelled conditions. Comments in the generated SQL mark each part:
- Window filter — restricts the query to the time window the incident was detected in. On a partitioned source, this also bounds the partition column.
- Segment conditions — restricts the query to the segment the incident was detected in.
- Segmentation filter — applies the filter configured on the segmentation attached to the source, if there is one.
- Validator filter — applies the filter configured on the validator, if there is one.
- Reference window and Reference filter — on a validator that compares against a second window or a second source, the same treatment for the reference side.
The query also orders records by how far they deviate from the expected value, so the most prominent outliers appear first, and applies a row limit.
Placeholders
The placeholders below appear in the generated query. Which of them you see depends on the validator and how it is configured — a validator with no segmentation, for example, has no segment to bind to.
| Placeholder | What it resolves to |
|---|---|
{{ validio_incident_window_start() }} | Start timestamp of the incident's data range — the window start. |
{{ validio_incident_window_end() }} | End timestamp of that same range. |
{{ validio_incident_reference_window_start() }} | Start of the reference window, derived from the incident window and the validator's reference lookback. Requires a validator with a reference lookback, on a tumbling window. |
{{ validio_incident_reference_window_end() }} | End of that reference window — the window start with the lookback offset applied. Same requirements as the reference window start. |
{{ validio_incident_partition_filter_start() }} | Timestamp for the source's partition-column filter on the incident window. |
{{ validio_incident_reference_partition_filter_start() }} | The same, but for the reference source's window. Referential Integrity validators only, and only when the reference window has a partition filter. |
{{ validio_incident_segment('<field>') }} | The incident's segment value for that field, quoted for the dialect. <field> must match the segmentation field's jsonpath exactly. |
{{ validio_validator_filter() }} | The validator's currently configured filter, rendered as a SQL expression. |
{{ validio_validator_reference_filter() }} | The reference filter configured on the validator — Categorical Distribution and Referential Integrity validators. |
{{ validio_segmentation_filter() }} | The filter configured on the segmentation attached to the source, rendered as a SQL expression. |
A placeholder used outside the validator types it supports makes the query fail with an error rather than being left in place, so a query that runs is a query whose placeholders all applied. The three filter placeholders resolve to 1 = 1 when no filter is configured — an expansion that looks empty is not a fault.
Placeholders expand differently per warehouse
A placeholder expands to whatever SQL dialect the source's warehouse expects, so the same saved query produces different literal SQL on BigQuery than it does on Snowflake. You do not need to account for this yourself.
Window Bounds
The incident's time window is bound by a pair of placeholders, compared against the Data-time field configured on the validator's window. On PostgreSQL, the generated condition looks like this:
CAST("UpdatedAt" AS TIMESTAMPTZ) >= {{ validio_incident_window_start() }}
AND CAST("UpdatedAt" AS TIMESTAMPTZ) < {{ validio_incident_window_end() }}Each placeholder resolves to a single timestamp literal, so the same condition against an hourly window becomes:
CAST("UpdatedAt" AS TIMESTAMPTZ) >= CAST('2020-01-01T00:00:00Z' AS TIMESTAMPTZ)
AND CAST("UpdatedAt" AS TIMESTAMPTZ) < CAST('2020-01-01T01:00:00Z' AS TIMESTAMPTZ)Because they resolve independently, you can use one without the other — to compare the incident's window against a different table, for instance, or to select the window bounds as columns alongside the records.
Segment Conditions
On a validator with a segmentation, the query is narrowed to the segment the incident was detected in:
CAST("City" AS TEXT) = {{ validio_incident_segment('City') }}Two things to note about this placeholder. Its argument is a single-quoted string — the segmentation field's jsonpath as configured, not a SQL identifier — and for a nested field that jsonpath is the dotted path, as in {{ validio_incident_segment('Delivery.Method') }}. And it resolves to the segment value, not to a whole condition, which is why it sits on the right-hand side of a comparison rather than standing alone.
Composite Segmentations
The placeholder always takes exactly one field. A segmentation on several fields produces one placeholder per field, each in its own condition:
/* Segment conditions */
AND CAST("City" AS TEXT) = {{ validio_incident_segment('City') }}
AND CAST("Country" AS TEXT) = {{ validio_incident_segment('Country') }}Each condition resolves on its own, so you can remove one to widen the query to every value of that field while keeping the incident's own value for the rest. A field name that is not part of the incident's segment makes the query fail with an error naming the fields it does carry.
Saving Requires a Window Placeholder
A saved query is only reusable if it still binds to the incident it is run against. A query that hardcodes the timestamps instead keeps returning the window it was written for, no matter which incident you open it on.
Validio enforces this: Save is unavailable unless the query contains at least one of validio_incident_window_start or validio_incident_window_end. If you have edited the placeholder away, restore it or use Reset to return to the generated query.
The placeholder must appear in its full
{{ ... }}form to count, with at most one space inside the braces. A mention of the name in a comment or a string does not bind anything, and does not make the query saveable.
A validator on a global window has no time window to bind to, so its generated debug query carries no window placeholder and cannot be saved for reuse. The query still runs, and you can still copy it out.
Seeing the Expanded Query
To read the query with its placeholders filled in, expand Executed SQL beneath the sample records in the debug dialog. It shows the literal SQL that produced the rows currently on screen, which is what you would run in your own database tools. See Debugging an Incident for how to load samples.
Custom SQL Validators Keep Their Own Template Functions
A Custom SQL validator has a generated debug query too: Validio wraps your metric query in a common table expression and selects the incident's records out of it. Your query is embedded as written, which means the template functions it uses — validio_window_id, validio_window_filter, validio_reference_window_filter, and validio_source_table — are still placeholders in the debug query, and are documented in Custom SQL Validator Query Syntax.
In a debug query they bind to the incident's window rather than to the validator's polling range, so the wrapped query returns the records behind the incident. They require a tumbling window, as they do when the validator runs.
Segment conditions work slightly differently here: because the wrapper selects from your query's result set, each condition compares the aliased segment column your query returns rather than the raw source column, while the placeholder's argument stays the source field's jsonpath. See Segmentation Fields for how those aliases are derived.
One name belongs to both sets
{{ validio_segmentation_filter() }}is both a debug query placeholder and a Custom SQL template function, and it means the same thing in either place — the filter configured on the segmentation attached to the source. Validio resolves both occurrences the same way, so you do not need to treat it differently depending on which query you are editing.
Fetching a Templated Query Through the API
If you build your own tooling on the Validio API, set templated: true on the ValidatorMetricDebugInfoInput of the validatorMetricDebugInfo query to receive the debug query in its placeholder form rather than with the incident's literal values. The default is false.
Templated queries are expanded automatically when executed, so a templated query can be passed straight back to validatorMetricDebugRecords without resolving it first.
Updated 2 days ago