QuerySQL
QuerySQL is Fixter’s read-only SQL dialect for querying your telemetry. It’s standard
SELECT SQL at heart; if you know SQL, you know most of it already. This page is the
exact surface: what you can query, the functions and operators Fixter adds, and where
the dialect deliberately differs from standard SQL.
QuerySQL runs everywhere Fixter accepts a query: the SQL mode on the
Logs page, the run_sql
MCP tool, and alert rule conditions.
Tables
Section titled “Tables”Three tables, matching the data model:
| Table | Queryable columns |
|---|---|
logs | timestamp, service, level, message, trace_id, span_id, parent_span_id, source_instance_id, log_id |
spans | timestamp, service, name, kind, status_code, status_message, trace_id, span_id, parent_span_id, source_instance_id, duration_ms |
metrics | timestamp, metric_name, service, source_instance_id, value |
duration_ms on spans is milliseconds (fractional). source_instance_id
identifies which instance of a service emitted the record. Your own attributes aren’t
listed here because they’re dynamic: they live behind each table’s attribute storage
(payload, and resource for resource attributes) and are addressed by name, as
described next. SELECT * expands to the table’s columns plus the raw attribute
object.
Dynamic attributes
Section titled “Dynamic attributes”Any attribute your instrumentation emits is queryable by name, as if it were a column, with no registration and no special syntax:
SELECT http_method, count(*) FROM logs WHERE http_method = 'POST' GROUP BY http_method- Dotted OpenTelemetry keys work as-is:
http.response.status_codeaddresses the attribute literally namedhttp.response.status_code. - Resource attributes (host, deployment, …) use the
resource.prefix:resource.host_name,resource.service.name. - In joins, qualify attributes with the table alias (
s.http_method); unqualified unknown names across two tables are rejected as ambiguous. Don’t name a join aliasresourceorpayload; those words are reserved for attribute access. - If an attribute name collides with a reserved SQL word, backtick-quote it:
`end`.
Functions
Section titled “Functions”Aggregates
Section titled “Aggregates”| Function | What it does |
|---|---|
count(*) | row count (on logs, counts unique log records) |
count_distinct(x) | distinct values of x |
sum(x), avg(x), min(x), max(x) | standard aggregates |
p50(x), p95(x), p99(x) | latency percentiles (approximate, fast at any scale) |
countIf(condition) | count of rows where the condition holds |
error_rate() | fraction of spans with status_code = 'ERROR' |
request_count() | span count (no arguments) |
error_burn_rate(budget) | error rate divided by your error budget; budget must be a positive number literal, e.g. error_burn_rate(0.01) |
latency_burn_rate(measure, threshold, budget) | fraction of rows where measure > threshold, divided by budget, e.g. latency_burn_rate(duration_ms, 500, 0.03) |
rate(x) | per-second rate of a counter metric over each minute |
value(x) | the natural aggregate of a metric’s samples (sum for counters, average for gauges) |
Scalar functions
Section titled “Scalar functions”| Function | What it does |
|---|---|
bucket(timestamp, '5m') | truncate a timestamp to fixed windows; the interval is <n>m, <n>h, or <n>d |
contains(column, 'needle') | fast token search inside one column |
matches('needle') | full-text search across the table’s searchable fields (message/name, your attributes, ids, service); takes a string literal |
regexp_extract(str, pattern[, group]) | extract a regex match (or capture group); returns NULL when no match |
now() | current time |
coalesce, if, nullif, concat, length, lower, upper, trim, substring, replace, abs, round, ceil, floor, greatest, least, cast | standard SQL behavior |
Operators beyond standard SQL
Section titled “Operators beyond standard SQL”| Operator | Meaning |
|---|---|
col =~ 'pattern' | case-insensitive glob match, where * matches any run and ? one character: message =~ '*timeout*' |
col = 'pat*tern' | an = whose right side contains * automatically becomes a glob match; without wildcards it stays exact. There is currently no way to =-match a value containing a literal *; use contains() for token matching in that case |
col != 'pat*tern' | same, negated |
col ILIKE 'pattern' | case-insensitive LIKE |
Standard comparison operators, LIKE, IN, BETWEEN (plain only; SYMMETRIC is
rejected), AND/OR/NOT, and CASE expressions all behave normally.
Differences from standard SQL
Section titled “Differences from standard SQL”SELECTonly. Nothing else parses: no DML, no DDL.- No
UNION, no CTEs (WITH), no window functions. - Joins: inner
JOINbetween tables and subqueries is supported;CROSS JOINand comma joins are rejected. Outer joins are not currently supported. - Subqueries work in
WHERE … IN (SELECT …)and as derived tables inFROM. GROUP BY/HAVING/ORDER BY/LIMIT/OFFSETbehave normally.- Identifiers are case-insensitive and quoted with backticks (MySQL-style), not double quotes.
- Timestamps: ISO-8601 string literals are understood anywhere and normalized to
UTC:
'2026-07-04T19:28:00+03:00'means2026-07-04 16:28:00UTC. All query times are UTC. - Comments (
--and/* */) are rejected by the API and MCP surfaces, so leave them out of queries you save or automate.
Examples
Section titled “Examples”Errors per service over the last hour, in 5-minute windows:
SELECT bucket(timestamp, '5m') AS window, service, count(*) AS errorsFROM logsWHERE level = 'ERROR' AND timestamp > '2026-07-17T09:00:00Z'GROUP BY bucket(timestamp, '5m'), serviceORDER BY bucket(timestamp, '5m')Full-text hunt across everything the logs table knows:
SELECT timestamp, service, message FROM logs WHERE matches('timeout') LIMIT 50p95 latency for one operation:
SELECT p95(duration_ms) FROM spans WHERE name = 'GET /checkout'Join spans to their logs through the trace id:
SELECT s.http_method, l.level, l.messageFROM spans s JOIN logs l ON s.trace_id = l.trace_idWHERE s.status_code = 'ERROR'SLO burn rate, as used in alert rules:
SELECT latency_burn_rate(duration_ms, 500, 0.03) FROM spans WHERE service = 'checkout'Discover what’s queryable, including your own attribute keys, with the
describe_schema MCP tool, or visually via the field rail on
the Logs page.