Skip to main content

Global filters

A dashboard is most useful when the viewer can re-scope it without editing SQL — "show me this for the West region", "show me this for last quarter". Global filters are the controls at the top of a dashboard that do that. They're cross-tile: change a filter, every tile that references it re-runs.

Filter types

TypeInputUse for
selectSingle-choice dropdownRegion, status, channel — pick one
multiSelectMulti-choice dropdownSeveral regions, several products — pick any combination
dateRangeTwo date pickersTime-bounded queries
dateSingle date pickerSnapshot date
textFree-text inputLIKE search by name, ad-hoc filters

Adding a filter

Open the dashboard in edit mode and click the Filters panel.

For each filter, configure:

  • ID — the symbol you'll reference in tile SQL (e.g. region, start_date).
  • Label — human-readable name shown above the input.
  • Type — one of the table above.
  • Default value — what's selected on first load.
  • Options source (for select / multiSelect):
    • Static — comma-separated list, or one-per-line. Best when the list is short and stable.
    • SQL — a query returning rows of (value, label). Best when options come from data and might change over time. Example:
      SELECT DISTINCT region AS value, region AS label
      FROM customers
      ORDER BY region;

Using the filter in tile SQL

Reference filters in SQL with ${filter_id}. QRY substitutes the current value at query time.

select and text — single value

SELECT *
FROM orders
WHERE region = '${region}';

If the user hasn't picked a value (and there's no default), the substitution is NULL-equivalent — guard with COALESCE or treat empty as "no filter":

WHERE ('${region}' = '' OR region = '${region}')

multiSelect — comma-separated list

SELECT *
FROM orders
WHERE region IN (${regions});

The ${regions} expands to a quoted list ('EMEA','NA','APAC'). Empty selection: same trick — (${regions}) IS NULL OR region IN (${regions}).

dateRange — two values

SELECT *
FROM orders
WHERE order_date BETWEEN '${start_date}' AND '${end_date}';

A dateRange filter exposes two ids, one with _start and one with _end suffix (or whatever you named them).

date — single value

SELECT *
FROM customers
WHERE snapshot_date = '${snapshot_date}';

Per-tile opt-out

Not every tile needs every filter. Reference only the filters you want; tiles that don't reference ${region} ignore changes to the region filter.

For complete opt-out (e.g. a header KPI that always shows the unfiltered total) just write the SQL without any ${...} substitution.

Gotchas

  • The id is case-sensitive. ${Region} and ${region} are different filters; if you copy a filter, double-check you don't have leftover references to the old id.
  • SQL injection isn't possible via filter values — QRY parameterises substitution server-side, not via raw string interpolation. But the surrounding SQL is your responsibility.
  • Empty multiSelect returns no rows with a literal IN (). Handle with the empty-guard trick above.
  • dateRange defaults respect the tenant's timezone — if your data uses UTC dates and your tenant defaults to Europe/Madrid, off-by-one issues are easy. Be explicit.

Common issues

A filter's options dropdown is empty. Static: you didn't enter values. SQL: the source query returned no rows, or it errored. Test the source query in a chat / SQL cell first.

${filter_id} shows up literally in the tile output. The id doesn't match exactly. Open the Filters panel, copy the id, paste — typos and case mismatches are the usual cause.

Tiles re-run on every keystroke of a text filter. By default text filters trigger on debounced change (~400ms). For very expensive queries, consider a select from a finite list instead.

Default values aren't loading on first open. Defaults respect the user's last-used filter values via localStorage. If you switched defaults but stale localStorage is overriding them, hard refresh.

See also

QRYA product of IXEN.