Complete reference for the Snuba Query Language (SnQL)
SnQL (Snuba Query Language) is a SQL-like declarative language for querying event data in Snuba. It provides a structured way to query entities with support for joins, subqueries, aggregations, and filtering.
MATCH { MATCH (transactions) SELECT avg(duration) AS avg_d BY transaction WHERE project_id = 1 AND timestamp >= toDateTime('2024-01-01') AND timestamp < toDateTime('2024-01-02')}SELECT max(avg_d) AS max_avg_duration
Subqueries allow you to compute aggregations over already-aggregated data. The outer query can only reference columns and aliases from the inner query’s SELECT clause.
Subqueries are useful for computing percentiles of aggregates, filtering on aggregated values, or performing multi-level rollups.
MATCH (e: events) -[grouped]-> (gm: groupedmessage)SELECT e.event_id, gm.status, count() AS event_countBY e.group_id, gm.statusWHERE e.project_id = 1 AND gm.project_id = 1 AND e.timestamp >= toDateTime('2024-01-01') AND e.timestamp < toDateTime('2024-01-02')
Multiple joins are supported:
MATCH (e: events) -[grouped]-> (g: groupedmessage), (e: events) -[assigned]-> (a: groupassignee)SELECT count() AS total BY e.project_id, g.idWHERE a.user_id = "user_123" AND e.timestamp >= toDateTime('2024-01-01') AND e.timestamp < toDateTime('2024-01-02')
When using joins:
Each entity must have a unique alias
All column references must be qualified with the alias (e.g., e.event_id)
Join keys and types are defined in the entity data model and cannot be customized per query
When using aggregation functions, use the BY clause for grouping:
SELECT count() AS total, sum(duration) AS total_durationBY project_id, transactionWHERE timestamp >= toDateTime('2024-01-01') AND timestamp < toDateTime('2024-01-02')
If using aggregations without a BY clause, only aggregation functions are allowed in SELECT. You cannot mix aggregations and non-aggregated columns without grouping.
-- Basic conditionsWHERE project_id = 1 AND timestamp >= toDateTime('2024-01-01') AND timestamp < toDateTime('2024-01-02')-- IN operatorWHERE project_id IN array(1, 2, 3) AND environment IN array('production', 'staging')-- LIKE operatorWHERE transaction LIKE '%/api/%' AND transaction NOT LIKE '%/health%'-- NULL checksWHERE error_message IS NOT NULL AND user_id IS NOT NULL-- Complex boolean logicWHERE (status_code >= 500 OR status_code = 429) AND (environment = 'production' OR environment = 'staging')
Most entities require specific conditions:
project_id: Usually required
timestamp range: Required for time-based entities
Example: timestamp >= toDateTime('2024-01-01') AND timestamp < toDateTime('2024-01-02')
-- Single columnORDER BY timestamp DESC-- Multiple columnsORDER BY project_id ASC, timestamp DESC-- By aggregationSELECT count() AS total BY transactionWHERE timestamp >= toDateTime('2024-01-01')ORDER BY total DESC
SELECT event_id, transaction, timestampBY transactionWHERE timestamp >= toDateTime('2024-01-01')ORDER BY timestamp DESCLIMIT 10 BY transaction -- Return up to 10 events per transactionLIMIT 100 -- Total limit across all groups
Groups time-based results into buckets. Used with special time-series columns:
MATCH (events)SELECT count() AS event_count BY timeWHERE timestamp >= toDateTime('2024-01-01') AND timestamp < toDateTime('2024-01-02')GRANULARITY 3600 -- Group by hour (3600 seconds)
Common granularity values:
60 - 1 minute
300 - 5 minutes
3600 - 1 hour
86400 - 1 day
The magic column name varies by entity. For events, it’s time. Check entity documentation for the correct column name.
SELECT count() AS total, min(duration) AS min_duration, max(duration) AS max_duration, avg(duration) AS avg_duration, sum(duration) AS total_durationBY project_id
SELECT has(tags.key, 'environment') AS has_env_tag, indexOf(tags.key, 'environment') AS env_tag_index, arrayElement(tags.value, indexOf(tags.key, 'environment')) AS env_value
MATCH (events)SELECT count() AS total_events, countIf(level = 'error') AS error_events, error_events / total_events AS error_rateBY project_id, environmentWHERE type != 'transaction' AND project_id IN array(1, 2, 3) AND timestamp >= toDateTime('2024-01-01T00:00:00') AND timestamp < toDateTime('2024-01-02T00:00:00')HAVING error_rate > 0.01ORDER BY error_rate DESCLIMIT 100
MATCH (transactions)SELECT quantile(0.95)(duration) AS p95_duration, quantile(0.50)(duration) AS p50_duration, count() AS transaction_countBY time, transactionWHERE project_id = 1 AND timestamp >= toDateTime('2024-01-01T00:00:00') AND timestamp < toDateTime('2024-01-02T00:00:00')GRANULARITY 3600ORDER BY time ASCLIMIT 10000
MATCH (e: events) -[grouped]-> (gm: groupedmessage)SELECT e.group_id, gm.status, count() AS event_count, max(e.timestamp) AS last_seen, uniq(e.user_id) AS affected_usersBY e.group_id, gm.statusWHERE e.project_id = 1 AND gm.project_id = 1 AND e.timestamp >= toDateTime('2024-01-01T00:00:00') AND e.timestamp < toDateTime('2024-01-02T00:00:00') AND gm.status = 0HAVING event_count > 10ORDER BY event_count DESCLIMIT 50
MATCH { MATCH (transactions) SELECT transaction, avg(duration) AS avg_duration, count() AS count BY transaction WHERE project_id = 1 AND timestamp >= toDateTime('2024-01-01T00:00:00') AND timestamp < toDateTime('2024-01-02T00:00:00') HAVING count > 100}SELECT transaction, avg_duration, countWHERE avg_duration > 1000ORDER BY avg_duration DESCLIMIT 10