Table (SQL)
A Table panel shows a query's raw rows — sortable, filterable, exportable. Unlike every other SQL panel type, there's no fixed column-alias convention (no bucket/value/name/lat/lon to match): whatever columns your query returns become the table's columns, discovered from the first row.
Columns
The same column configuration described in Panel types applies here as the table's own primary configuration, not just a drill-down's: rename a column, override its type, and open its gear menu for Hide column / Show filter row / Show as. A column whose values are JSON objects or arrays auto-detects as json or table and renders as a click-to-expand popover rather than a raw text blob. See Column modifiers for a full, screenshotted walkthrough of every option in that menu.
Sorting, filtering, and export — live, not saved
Clicking a column header sorts by it (numbers compare numerically, everything else as text); the optional filter row narrows visible rows by range, substring, or date. None of this is part of the panel's saved configuration — two people looking at the same table can have it sorted and filtered completely differently, the same as any ordinary spreadsheet. A CSV button (on by default, toggleable) exports every currently visible row — sorted and filtered, across every page, not just the one on screen.
Row highlight triggers
The same row-tinting rules described in Panel types — a column, a condition, a color — apply directly to a table panel's own rows.
Example: Top customers (enriched)
SELECT phone, name, customer_type, is_vip, sum(item_qty * item_price) AS total_spent, toJSONString(arrayReverseSort(t -> t.1, groupArray( CAST(tuple(toString(sale_time), item_name, item_qty, item_price) AS Tuple(time String, item String, qty Int64, price Float64)) ))) AS purchasesFROM ( … unnest each sale's items, one row per line item … )GROUP BY phone, name, customer_type, is_vipORDER BY total_spent DESCLIMIT 20
“Top customers (enriched)” — phone, name and customer_type are enrichment output; Purchases is a nested table column.
phone, name, and customer_type aren't raw event fields — they're written into metadata by a CRM-lookup enrichment function at ingestion time. purchases is the more advanced trick: groupArray collects each customer's individual line items into an array, CAST(tuple(…) AS Tuple(named fields)) is what makes toJSONString serialize it as an array of named objects instead of anonymous arrays-of-arrays, and arrayReverseSort orders that array most-recent-first. The result auto-detects as a table-type column and renders as its own small nested table when clicked — one row per item actually bought, not an opaque JSON blob.
No drill-down, and why
Table panels don't have a drill-down query, deliberately: every other panel type's drill-down exists to answer “what are the underlying rows behind this one aggregated point?” A table's rows already are that underlying detail — there's nothing further to drill into.
Last updated 9 July 2026