Joins across signal types in four lines. A gentle, copy-paste-ready introduction.
HelixQL looks intimidating until you write your first query and realize it reads like a sentence. Let us build one together, one line at a time.
Start with a source
Every query begins by naming a signal — metrics, logs, or traces. That is your table. From there you pipe it through transformations, left to right, like a Unix shell.
traces
| where service == "checkout"
| stats p95 = percentile(duration, 95) by route
| sort p95 desc
Read it out loud
"Take traces, keep the ones from checkout, compute the 95th-percentile duration per route, and sort by the slowest." That is the whole query, and the syntax maps one-to-one to the sentence.
Now join across signals
The moment HelixQL earns its keep is when you join two signal types on a shared key:
traces
| where duration > 2s
| join (logs | where level == "error") on trace_id
Where to go next
You now know sources, filters, aggregations, and joins — enough to answer most real questions. The rest of the language is variations on these four ideas. Open the query editor and change a value; the fastest way to learn HelixQL is to break a working query and fix it.