---
title: "ClickHouse query editor | Grafana Plugins documentation"
description: "This document describes the ClickHouse query editor"
---

> For a curated documentation index, see [llms.txt](/llms.txt). For the complete documentation index, see [llms-full.txt](/llms-full.txt).

# ClickHouse query editor

This document explains how to use the ClickHouse query editor to build and run queries. You can access the query editor from [Explore](/docs/grafana/latest/visualizations/explore/) to run ad hoc queries, or when you add or edit a panel and select the ClickHouse data source.

## Before you begin

- [Configure the ClickHouse data source](/docs/plugins/grafana-clickhouse-datasource/latest/configure/).
- Ensure your ClickHouse user has read access to the databases and tables you want to query.

## Query editor elements

The query editor appears in [Explore](/docs/grafana/latest/visualizations/explore/) when you select the ClickHouse data source, or when you add or edit a panel and select ClickHouse. It includes the following elements.

Expand table

| Element          | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
|------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Editor type**  | Switch between **SQL** (write raw SQL) and **Query builder** (build queries with drop-downs and filters).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| **Run Query**    | Runs the current query and refreshes the panel. In the SQL editor you can also use **Ctrl+Enter** (Windows/Linux) or **Cmd+Enter** (macOS).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| **Query type**   | Choose the result format: **Table**, **Logs**, **Time series**, or **Traces**. This sets how Grafana interprets and visualizes the results. Available in both SQL and Query builder modes.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| **Min interval** | A lower bound for this query’s interval, as one number plus a unit of `ms`, `s`, `m`, `h`, `d` or `w` (for example `10s`, `5m`, `1d`). It raises the interval Grafana derives from the time range and panel width, so `$__timeInterval`, `$__timeInterval_ms`, `$__interval_s`, `$__timeGroup`, `$__interval` and `$__interval_ms` never bucket finer than the value you set; it never lowers the interval. Leave it empty to use the interval Grafana picks. Explore has no panel-level **Min interval**, so this is the only way to control bucket size there. The floor applies in Explore, dashboard panels and alert rules alike, and to the Explore log volume histogram. Values outside that grammar, including `1M` and `1y`, are rejected in the editor and ignored by the backend. |

**In SQL mode:**

- **SQL editor**: A code editor where you write ClickHouse SQL. It provides schema suggestions (databases, tables, columns) as you type. If SQL validation is enabled in the data source settings, the editor marks invalid syntax.
- **Format code**: Use the editor toolbar to format your SQL.
- **Query type**: Select **Table**, **Logs**, **Time series**, or **Traces** so the panel uses the correct visualization.

**In Query builder mode:**

- **Database** and **Table**: Select the database and table to query from the drop-downs.
- **Query type**: Select **Table**, **Logs**, **Time series**, or **Traces**. The builder shows options that match the type (for example, time column and value columns for time series; columns, filters, group by, and order by for tables).
- **Type-specific options**: Configure columns, filters, grouping, sorting, limit (max rows), and (for traces) trace ID. The builder generates the SQL for you.
- **SQL preview**: At the bottom of the builder, you can see the generated SQL. You can switch to SQL mode to edit it manually.

If the data source is configured in **Single source** mode, the query builder uses the configured logs or traces source as its default database, table, and column mapping.

### Compact query mode

When a data source is configured in **Single source** mode for logs or traces, the query builder uses a compact editor focused on the configured source. The compact editor omits database, table, and query type controls because those values come from the data source configuration.

For logs, use the search field to filter log message text. Use **Add filter** to add column filters, and use the advanced options to adjust sorting and the result limit.

Compact filters use Grafana’s selected time range automatically. The configured time fields are not shown in the filter field list because the query builder manages the time range filter for you.

Filter operators are tailored to the selected field type. Numeric fields offer comparison operators such as `>`, `<`, `>=`, `<=`, `=`, and `!=`. String fields offer text matching and equality operators such as **contains**, **does not contain**, `=`, and `!=`.

The compact editor also includes a generated SQL preview. Use **Copy** to copy the generated query, or **Open in SQL editor** to switch to the SQL editor for manual customization.

## Build queries

You can build queries using the **SQL editor** (raw SQL) or the **Query builder**. Queries can include macros for dynamic parts such as time range filters.

### Query builder modes

When using the query builder, the available options depend on the selected **query type**:

Expand table

| Query type      | Builder modes                    | Description                                                                                                                                                             |
|-----------------|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Table**       | **Simple** or **Aggregate**      | Simple returns raw rows. Aggregate lets you add functions like `count()`, `avg()`, and `GROUP BY`.                                                                      |
| **Time series** | **Trend**                        | Automatically groups by time using `$__timeInterval()` and applies aggregate functions. Select a time column, one or more value columns, and optional grouping columns. |
| **Logs**        | **Simple** or **Aggregate**      | Simple returns log rows. Aggregate supports grouping for log volume histograms.                                                                                         |
| **Traces**      | **Trace ID** or **Trace search** | Trace ID fetches a single trace by ID. Trace search finds traces matching filters (service name, duration, time range).                                                 |

### Aggregate mode

Selecting **Aggregate** as the builder mode (available for the **Table** and **Logs** query types) computes aggregate functions with a `GROUP BY`. The builder exposes these fields:

Expand table

| Field          | Description                                                                                                                                                    |
|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Columns**    | Plain columns to return alongside the aggregates.                                                                                                              |
| **Aggregates** | One or more aggregate functions to compute, such as `count()`, `avg(Duration)`, or `sum(Bytes)`. Click **Aggregate** to add each one, and optionally alias it. |
| **Group By**   | Columns to group the aggregates by. The drop-down lists every column in the selected table, so it can be long on wide tables.                                  |
| **Order By**   | Columns or aggregates to sort by, each ascending or descending.                                                                                                |
| **Limit**      | Maximum number of rows to return. Set to `0` to omit the `LIMIT` clause.                                                                                       |
| **Filters**    | `WHERE` conditions, combined with `AND` or `OR`.                                                                                                               |

The builder assembles these into a single query, selecting the grouped columns before the aggregate expressions. For example, grouping by `Datacenter` with a row count and average duration produces:

SQL ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```sql
SELECT
  Datacenter,
  count() AS count,
  avg(Duration) AS avg_duration
FROM mydb.GENERATED_LOGS
GROUP BY Datacenter
ORDER BY count DESC
LIMIT 1000
```

### Logs query builder

The logs query builder provides structured fields for common log exploration patterns. When **Use OTel** is enabled, the builder pre-fills column mappings for [OpenTelemetry ClickHouse exporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/clickhouseexporter) tables.

Expand table

| Field                  | Description                                                                                                                                                                                                    |
|------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Time column**        | The high-precision timestamp column used for sorting log rows.                                                                                                                                                 |
| **Filter Time column** | A lower-precision column for fast time range filtering (for example, a `Date` or `DateTime` column indexed for partition pruning). Using a separate filter column can significantly improve query performance. |
| **Log Level column**   | The column that contains the log severity level.                                                                                                                                                               |
| **Log Message column** | The column that contains the log message body.                                                                                                                                                                 |

You can also filter by log message text using the search field, and add column filters for resource, scope, or log attributes.

Columns configured through the data source **Columns** setting are projected into new logs queries automatically, so they appear here as browsable, filterable fields without editing the query. See [Logs configuration](/docs/plugins/grafana-clickhouse-datasource/latest/configure/#logs-configuration).

### Traces query builder

The traces query builder supports two modes:

- **Trace ID mode**: Enter a trace ID (or use a template variable like `$traceId`) to fetch all spans for that trace.
- **Trace search mode**: Filter traces by service name, span name, duration range, and time range.

When **Use OTel** is enabled, column mappings are pre-filled for the OpenTelemetry schema. You can also configure:

Expand table

| Field              | Description                                                                                     |
|--------------------|-------------------------------------------------------------------------------------------------|
| **Duration unit**  | The unit for the duration column (`seconds`, `milliseconds`, `microseconds`, or `nanoseconds`). |
| **Flatten nested** | Enable if your traces table was created with `flatten_nested=1`.                                |
| **Events prefix**  | Prefix for event columns (for example, `Events.Timestamp`, `Events.Name`).                      |
| **Links prefix**   | Prefix for link columns (for example, `Links.TraceId`, `Links.SpanId`).                         |

### JSON-type OTel attribute columns

The [OpenTelemetry ClickHouse exporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/clickhouseexporter/README.md#experimental-json-support) supports an experimental mode (enabled by setting `enable_json_type: true` in the collector config) that stores attribute maps (`SpanAttributes`, `ResourceAttributes`, `LogAttributes`, `ScopeAttributes`, and event/link attribute columns) as ClickHouse’s native `JSON` type instead of `Map(String, String)`.

The plugin auto-detects JSON-typed attribute columns at query time. No manual configuration is required.

**How auto-detection works:**

When you open a trace or log query in OTel mode, the plugin runs `DESC TABLE` on the target table and checks whether the attribute columns have a `JSON` type. If they do, the plugin:

- Returns `SpanAttributes` and `ResourceAttributes` as raw JSON objects and flattens them client-side into `{key, value}` pairs. Nested keys produced by the ClickHouse JSON type (for example, `{"http": {"method": "GET"}}` from an original key of `http.method`) are rejoined with a dot.
- Wraps event and link attribute columns in the SQL with `toJSONString()` and expands them client-side, avoiding the `mapKeys()` function that does not work on `JSON`-typed columns.

If the table uses `Map(String, String)` attribute columns (the default for ClickHouse older than 26 or when `enable_json_type` is not set), the plugin uses the standard `mapKeys()` path and nothing changes.

**Filter path discovery:**

The [OTel ClickHouse exporter JSON schema](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/clickhouseexporter/README.md#experimental-json-support) ships a companion `Array(LowCardinality(String))` column for each JSON attribute column (for example, `SpanAttributesKeys` alongside `SpanAttributes`). These pre-materialized key arrays have a bloom-filter index and are used by the plugin for fast filter path discovery. If the companion column is present, the plugin queries `arrayJoin(SpanAttributesKeys)` instead of `JSONAllPaths(SpanAttributes)`.

> Note
> 
> JSON-type attribute columns require ClickHouse 26 or later and the OTel Collector exporter configured with `enable_json_type: true`. The plugin detects the column type automatically and falls back to `Map(String, String)` handling for older schemas.

### Log volume and logs sample

In Explore with the **Logs** query type, Grafana automatically shows a **log volume** histogram above the log results and can display a **logs sample** panel. These supplementary queries are generated by the plugin and run alongside your main query.

Log volume works for queries written in either the **query builder** or the **SQL editor**. For a SQL editor query, the plugin aggregates over your query as a subquery, so your filters, joins, CTEs, macros, and template variables are preserved, and it removes your `ORDER BY` and `LIMIT` so the histogram covers the whole selected time range instead of just the rows your query returns. Log level series require a log level column configured under **Logs** in the data source settings and projected by your query.

When the plugin cannot guarantee a correct count it declines, and Grafana draws its row-based histogram instead. It declines when:

- No log timestamp column is configured, so there is nothing to bucket on. A data source configured with only a lower-precision *filter* timestamp declines too.
- The query does not select the configured timestamp column first, or selects it as an expression.
- A `SELECT *` query reads anything other than the configured logs table, including a CTE that shadows its name.
- The query uses a construct that changes which rows exist: `SETTINGS`, `LIMIT n BY`, `WITH FILL`, `WITH TOTALS`, `UNION`, an interval macro, or more than one statement.
- The query ends in a row limit the plugin cannot parse, such as `LIMIT (500)` or `LIMIT ${maxRows}`.

Grafana renders one histogram per panel, so a single query that cannot be aggregated makes every query in that panel fall back, including query builder queries.

> Note
> 
> Falling back to the row-based histogram requires Grafana 12.4.0 or later. On earlier versions the aggregated histogram still works, but a query the plugin declines shows no volume chart at all.

## Time series

For time series visualizations, your query must include a `datetime` column. Use an alias of `time` for the timestamp column. Grafana treats timestamp rows without an explicit time zone as UTC. Any column other than `time` is treated as a value column.

Example of a single series bucketed to the panel interval (replace `mgbench.logs1` with your database and table):

SQL ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```sql
SELECT $__timeInterval(log_time) AS time, avg(disk_free) AS avg_disk_free
FROM mgbench.logs1
WHERE $__timeFilter(log_time)
GROUP BY time
ORDER BY time
```

## Multi-line time series

To create multi-line time series, the query must return at least 3 columns in this order:

1. A `datetime` column with an alias of `time`
2. A column to group by (for example, category or label)
3. One or more metric columns

Example (replace `mgbench.logs1` with your database and table):

SQL ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```sql
SELECT log_time AS time, machine_group, avg(disk_free) AS avg_disk_free
FROM mgbench.logs1
GROUP BY machine_group, log_time
ORDER BY log_time
```

## Tables

Table visualizations are available for any valid ClickHouse query. Select **Table** in the panel visualization options to view results in tabular form.

## Visualize logs with the Logs panel

To use the Logs panel, your query must return a time column and one or more string columns. Set the **Query type** to **Logs** so Grafana renders the results in the logs visualization. In the query builder, select the **Logs** query type; in the SQL editor, set the **Query type** (Format) to **Logs**. Aliasing the message column to `body`, the time column to `timestamp`, and the level column to `level` matches what the Logs panel and the query builder expect.

Example (replace `mydb.logs` with your database and table):

SQL ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```sql
SELECT
  Timestamp AS timestamp,
  Body AS body,
  SeverityText AS level,
  ServiceName
FROM mydb.logs
WHERE $__timeFilter(Timestamp)
ORDER BY Timestamp DESC
LIMIT 1000
```

## Visualize traces with the Traces panel

To use the Traces panel, your data must meet the [requirements of the traces panel](/docs/grafana/latest/explore/trace-integration/#data-api). Set the **Query type** to **Traces** when building the query.

If you use the [OpenTelemetry Collector and ClickHouse exporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/clickhouseexporter), the following query returns the required column names (case sensitive). Replace the trace ID in the WHERE clause with your trace ID or a template variable (for example `$traceId`).

For tables with **`Map(String, String)` attribute columns** (the default schema):

SQL ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```sql
SELECT
  TraceId AS traceID,
  SpanId AS spanID,
  SpanName AS operationName,
  ParentSpanId AS parentSpanID,
  ServiceName AS serviceName,
  Duration / 1000000 AS duration,
  Timestamp AS startTime,
  arrayMap(key -> map('key', key, 'value', SpanAttributes[key]), mapKeys(SpanAttributes)) AS tags,
  arrayMap(key -> map('key', key, 'value', ResourceAttributes[key]), mapKeys(ResourceAttributes)) AS serviceTags,
  if(StatusCode IN ('Error', 'STATUS_CODE_ERROR'), 2, 0) AS statusCode
FROM otel.otel_traces
WHERE TraceId = '61d489320c01243966700e172ab37081'
ORDER BY startTime ASC
```

For tables with **`JSON`-type attribute columns** (ClickHouse 26+ with `enable_json_type: true`), select the attribute columns directly, because `mapKeys()` does not work on the `JSON` type:

SQL ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```sql
SELECT
  TraceId AS traceID,
  SpanId AS spanID,
  SpanName AS operationName,
  ParentSpanId AS parentSpanID,
  ServiceName AS serviceName,
  Duration / 1000000 AS duration,
  Timestamp AS startTime,
  SpanAttributes AS tags,
  ResourceAttributes AS serviceTags,
  if(StatusCode IN ('Error', 'STATUS_CODE_ERROR'), 2, 0) AS statusCode
FROM otel.otel_traces
WHERE TraceId = '61d489320c01243966700e172ab37081'
ORDER BY startTime ASC
```

> Tip
> 
> When using the **Query builder** in OTel mode, the plugin detects the attribute column type automatically and generates the correct SQL for both `Map` and `JSON` schemas. You do not need to write the trace query manually.

## Column roles

When you use the Query builder with the **Logs**, **Time series**, or **Traces** query type, each built-in column slot is mapped to a *semantic role*. The builder renames your columns to the fixed aliases Grafana’s panels expect, so the same panel can visualize data from any ClickHouse schema once you tell it which columns play which roles.

For example, choosing your `EventTime` column as the **Time** role for a Logs query produces `SELECT EventTime AS timestamp, ...`; the Logs panel then sorts on `timestamp` without needing to know your real column name.

### Logs query type

Expand table

| Role          | SQL alias   | OTel column    | Common non-OTel names                                 |
|---------------|-------------|----------------|-------------------------------------------------------|
| **Time**      | `timestamp` | `Timestamp`    | `timestamp`, `event_time`, `@timestamp`, `created_at` |
| **Message**   | `body`      | `Body`         | `message`, `msg`, `log_message`                       |
| **Log Level** | `level`     | `SeverityText` | `level`, `severity`, `severity_text`, `log_level`     |

Optional additional columns (OTel mode only): `TraceId` → `traceID`, plus the attribute maps `ResourceAttributes`, `ScopeAttributes`, and `LogAttributes`.

### Time series query type

Expand table

| Role     | Description                                                                                                                                     |
|----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| **Time** | Timestamp used to order and bucket the series. Must be a `DateTime` or `DateTime64` column. The panel time range filter applies to this column. |

Any other selected columns become value series. In **Aggregate** mode the builder produces `GROUP BY` on the time column and the aggregated columns.

### Traces query type

Trace-panel column aliases are fixed; choose the table columns that play each role.

Expand table

| Role               | SQL alias       | OTel column          | Common non-OTel names                      |
|--------------------|-----------------|----------------------|--------------------------------------------|
| **Trace ID**       | `traceID`       | `TraceId`            | `trace_id`, `traceId`                      |
| **Span ID**        | `spanID`        | `SpanId`             | `span_id`, `spanId`                        |
| **Parent Span ID** | `parentSpanID`  | `ParentSpanId`       | `parent_span_id`                           |
| **Service Name**   | `serviceName`   | `ServiceName`        | `service`, `service_name`                  |
| **Operation Name** | `operationName` | `SpanName`           | `operation`, `operation_name`, `span_name` |
| **Start Time**     | `startTime`     | `Timestamp`          | `start_time`, `timestamp`                  |
| **Duration Time**  | `duration`      | `Duration`           | `duration`, `duration_ns`, `duration_ms`   |
| **Tags**           | `tags`          | `SpanAttributes`     | `tags`, `attributes`                       |
| **Service Tags**   | `serviceTags`   | `ResourceAttributes` | `resource`, `resource_attributes`          |
| **Kind**           | `kind`          | `SpanKind`           | `kind`, `span_kind`                        |
| **Status Code**    | `statusCode`    | `StatusCode`         | `status`, `status_code`                    |
| **Status Message** | `statusMessage` | `StatusMessage`      | `status_message`                           |

Set the **Duration Unit** to match the units your column stores (OTel uses nanoseconds; other schemas often use milliseconds or seconds). The builder converts durations to milliseconds for the trace panel.

To avoid re-mapping roles for every query, configure defaults under **Data source settings → Logs** and **Data source settings → Traces**, or use **Single source** mode when a data source is dedicated to one logs or traces table. Enabling **OTel** mode populates every role with the OTel-conventional column name automatically.

## Macros

Macros simplify query syntax and add dynamic parts such as dashboard time range filters. The plugin replaces macros with the corresponding SQL before the query is sent to ClickHouse.

Example query using a time filter macro (replace `test_data` and `date_time` with your table and timestamp column):

SQL ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```sql
SELECT date_time, data_stuff
FROM test_data
WHERE $__timeFilter(date_time)
```

Expand table

| Macro                                        | Description                                                                                      | Output example                                                                                                                            |
|----------------------------------------------|--------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|
| `$__dateFilter(columnName)`                  | Filters by the panel date range using the given column.                                          | `date >= toDate('2022-10-21') AND date <= toDate('2022-10-23')`                                                                           |
| `$__timeFilter(columnName)`                  | Filters by the panel time range (seconds).                                                       | `time >= toDateTime(1415792726) AND time <= toDateTime(1447328726)`                                                                       |
| `$__timeFilter_ms(columnName)`               | Filters by the panel time range (milliseconds).                                                  | `time >= fromUnixTimestamp64Milli(1415792726123) AND time <= fromUnixTimestamp64Milli(1447328726456)`                                     |
| `$__dateTimeFilter(dateColumn, timeColumn)`  | Combines date and time filters for separate Date and DateTime columns.                           | `(date >= toDate('2022-10-21') AND date <= toDate('2022-10-23')) AND (time >= toDateTime(1415792726) AND time <= toDateTime(1447328726))` |
| `$__dt(dateColumn, timeColumn)`              | Shorthand alias for `$__dateTimeFilter`.                                                         | Same as `$__dateTimeFilter`.                                                                                                              |
| `$__timeFrom(columnName)`                    | Filters rows at or after the panel time range start.                                             | `time >= toDateTime(1415792726)`                                                                                                          |
| `$__timeTo(columnName)`                      | Filters rows at or before the panel time range end.                                              | `time <= toDateTime(1447328726)`                                                                                                          |
| `$__timeGroup(columnName, period)`           | Buckets the time column into fixed intervals for grouping. Accepts a duration such as `5m`.      | `toStartOfInterval(toDateTime(column), INTERVAL 300 second)`                                                                              |
| `$__fromTime`                                | Start of the panel time range as `DateTime`.                                                     | `toDateTime(1415792726)`                                                                                                                  |
| `$__toTime`                                  | End of the panel time range as `DateTime`.                                                       | `toDateTime(1447328726)`                                                                                                                  |
| `$__fromTime_ms`                             | Start of the panel time range as `DateTime64(3)`.                                                | `fromUnixTimestamp64Milli(1415792726123)`                                                                                                 |
| `$__toTime_ms`                               | End of the panel time range as `DateTime64(3)`.                                                  | `fromUnixTimestamp64Milli(1447328726456)`                                                                                                 |
| `$__interval_s`                              | Panel interval in seconds.                                                                       | `20`                                                                                                                                      |
| `$__timeInterval(columnName)`                | Interval from panel time range (seconds), for grouping.                                          | `toStartOfInterval(toDateTime(column), INTERVAL 20 second)`                                                                               |
| `$__timeInterval_ms(columnName)`             | Interval from panel time range (milliseconds), for grouping.                                     | `toStartOfInterval(toDateTime64(column, 3), INTERVAL 20 millisecond)`                                                                     |
| `$__conditionalAll(condition, $templateVar)` | Uses the first parameter when the template variable does not select all values; otherwise `1=1`. | `condition` or `1=1`                                                                                                                      |

You can also use brace notation `{}` when the macro parameter must contain a query or other expression.

### Statement macros

Statement macros expand to a complete query rather than a single expression. Write the macro in place of the `SELECT` clause, followed by the rest of the query. Everything from the macro to the end of the query is replaced with a generated statement that applies the panel time filter, buckets timestamps with `$__timeInterval()`, and returns one series per key value.

Expand table

| Macro                                         | Description                                                                                                                                                                                   |
|-----------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `$__columns(timeColumn, key, value)`          | One series per `key` value, with `value` aggregated per time bucket.                                                                                                                          |
| `$__rateColumns(timeColumn, key, value)`      | Like `$__columns`, but divides the aggregated value by the number of seconds since the previous bucket of the same series. Use it to smooth gauge-style values into per-second rates.         |
| `$__perSecondColumns(timeColumn, key, value)` | Per-second rate of a monotonic counter per series, like the Prometheus `rate()` function. `value` is aggregated with `max()`.                                                                 |
| `$__increaseColumns(timeColumn, key, value)`  | Raw increase of a monotonic counter per bucket and series, like the Prometheus `increase()` function. `value` is aggregated with `max()`.                                                     |
| `$__lttb(buckets, x, y)`                      | Downsamples dense series with ClickHouse’s `lttb()` aggregate function (Largest-Triangle-Three-Buckets). Pass a bucket count, or `auto` to derive one from the panel time range and interval. |

For example, the following query returns one series per service, with the request count aggregated into each time bucket:

SQL ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```sql
$__columns(EventTime, ServiceName, count() AS c) FROM requests WHERE ServiceName != ''
```

For a monotonic counter, the following query charts the per-second request rate of every service:

SQL ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```sql
$__perSecondColumns(EventTime, ServiceName, RequestsTotal) FROM requests WHERE ServiceName != ''
```

Usage notes:

- The key column is converted to a string and defaults to the alias `metric`. Provide your own alias with `AS`, for example `ServiceName AS service`. The value column defaults to the alias `value`.
- A `WHERE` clause in the query is combined with the generated time filter. `HAVING`, `ORDER BY`, `LIMIT`, and `SETTINGS` clauses are kept. The `*Columns` macros generate their own `GROUP BY`, so the query must not contain one. `$__lttb` runs a `GROUP BY` in a subquery so the rows are aggregated before they are downsampled, which allows an aggregate `y` expression such as `avg(value)`.
- The first point of each series, and any point where a counter resets, is returned as `nan`.
- Only one statement macro can be used per query, and it must be at the top level of the statement rather than inside a subquery. Text before the macro, such as a `WITH` clause, is preserved.

## Next steps

- [ClickHouse template variables](/docs/plugins/grafana-clickhouse-datasource/latest/template-variables/): Use variables in dashboards and queries.
- [Configure the ClickHouse data source](/docs/plugins/grafana-clickhouse-datasource/latest/configure/): Connection and authentication options.
