ClickStack 支持基于原始 SQL 查询的可视化。这让你能够完全控制查询逻辑,同时仍可与仪表板级别的时间范围、过滤器和图表渲染集成。
当你需要实现内置 Chart Explorer 不支持的功能时,基于 SQL 的可视化就非常有用——例如,关联多个表,或构建图表构建器不支持的复杂聚合。
创建基于 SQL 的可视化
要创建基于 SQL 的可视化,请打开仪表板卡片编辑器并选择 SQL 选项卡。

接下来:
- 选择一个要用于运行查询的 ClickHouse 连接。
- 也可以选择一个 Source —— 这样就能通过
$__filters宏将仪表板级过滤器应用到图表。 - 在编辑器中编写 SQL 查询,使用查询参数和宏与仪表板的时间范围、过滤器及变量集成。
- 点击 play 按钮预览结果,然后点击 Save。
查询参数
查询参数 允许你在 SQL 中引用当前仪表板的时间范围和粒度。它们使用 ClickHouse 的参数化查询语法:{paramName:Type}。
可用参数
可用参数取决于图表类型:
折线图和堆叠条形图:
| 参数 | 类型 | 描述 |
|---|---|---|
{startDateMilliseconds:Int64} |
Int64 | 仪表板日期范围的开始时间 (自纪元以来的毫秒数) |
{endDateMilliseconds:Int64} |
Int64 | 仪表板日期范围的结束时间 (自纪元以来的毫秒数) |
{intervalSeconds:Int64} |
Int64 | 时间分桶大小 (以秒为单位,基于粒度) |
{intervalMilliseconds:Int64} |
Int64 | 时间分桶大小 (以毫秒为单位,基于粒度) |
表格、饼图和数值图表:
| 参数 | 类型 | 描述 |
|---|---|---|
{startDateMilliseconds:Int64} |
Int64 | 仪表板日期范围的开始时间 (自纪元以来的毫秒数) |
{endDateMilliseconds:Int64} |
Int64 | 仪表板日期范围的结束时间 (自纪元以来的毫秒数) |
宏
宏是会展开为常见 ClickHouse SQL 表达式的简写。它们以前缀 $__ 开头,并会在查询发送到 ClickHouse 之前完成替换。
时间边界宏
这些宏返回一个 ClickHouse 表达式,表示 仪表板 的开始时间或结束时间。它们不接受任何参数。
| Macro | 展开为 | 列类型 |
|---|---|---|
$__fromTime |
toDateTime(fromUnixTimestamp64Milli({startDateMilliseconds:Int64})) |
DateTime |
$__toTime |
toDateTime(fromUnixTimestamp64Milli({endDateMilliseconds:Int64})) |
DateTime |
$__fromTime_ms |
fromUnixTimestamp64Milli({startDateMilliseconds:Int64}) |
DateTime64 |
$__toTime_ms |
fromUnixTimestamp64Milli({endDateMilliseconds:Int64}) |
DateTime64 |
$__interval_s |
{intervalSeconds:Int64} |
Int64 |
时间过滤宏
这些宏会生成一个 WHERE 子句片段,用于按仪表板的时间范围过滤某一列。
| Macro | Description |
|---|---|
$__timeFilter(column) |
按仪表板时间范围过滤 DateTime 列 |
$__timeFilter_ms(column) |
按仪表板时间范围过滤 DateTime64 (毫秒) 列 |
$__dateFilter(column) |
按仪表板时间范围过滤 Date 列 |
$__dateTimeFilter(dateCol, timeCol) |
使用单独的 Date 列和 DateTime 列进行过滤 |
$__dt(dateCol, timeCol) |
$__dateTimeFilter 的别名 |
$__timeFilter(TimestampTime) 的展开示例:
TimestampTime >= toDateTime(fromUnixTimestamp64Milli({startDateMilliseconds:Int64}))
AND TimestampTime <= toDateTime(fromUnixTimestamp64Milli({endDateMilliseconds:Int64}))时间间隔宏
这些宏会将时间戳列分组到与仪表板粒度相匹配的时间间隔中。它们通常用于时间序列图表的 SELECT 和 GROUP BY 子句。仅适用于 折线图 和 Stacked-bar 可视化。
| Macro | Description |
|---|---|
$__timeInterval(column) |
将 DateTime 列按 intervalSeconds 的时间间隔分组 |
$__timeInterval_ms(column) |
将 DateTime64 列按 intervalMilliseconds 的时间间隔分组 |
$__timeInterval(TimestampTime) 的展开示例:
toStartOfInterval(toDateTime(TimestampTime), INTERVAL {intervalSeconds:Int64} second)仪表板过滤器宏
| Macro | Description |
|---|---|
$__filters |
替换为仪表板级过滤条件 (需要选择一个 Source) |
当在图表中选择了 Source 且仪表板过滤器已启用时,$__filters 会展开为对应的 SQL WHERE 条件。未选择 Source 或未应用任何过滤器时,它会展开为 (1=1),因此始终可以安全地将其包含在 WHERE 子句中。
只有启用了 Broadcast filter condition 的过滤器才会由 $__filters 应用。作为变量公开的过滤器会被显式引用,如下所述。
仪表板变量宏
当仪表板过滤器可用作变量时,可在查询的任何位置引用其选中值。只有属于声明了至少一个变量的仪表板的卡片才能使用这些宏。
| 宏 | 描述 |
|---|---|
$__filter($<variable>) |
当为 variable 选择了值时,展开为 toString(<filter expression>) IN ($variable);否则展开为 1=1。 |
$__filter(<expression>, $<variable>) |
当为 variable 选择了值时,展开为 <expression> IN ($variable);否则展开为 1=1。 |
$__conditionalAll(<condition>, $<variable>) |
当变量有选中值时,展开为 <condition>;否则展开为 1=1。 |
变量值也可直接插值为 $name 或 ${name},并可选择指定格式:${name:sqlstring} (默认) 、${name:csv} 或 ${name:regex}。在默认格式下,未选择任何值时,直接引用会呈现为 NULL;因此,凡是需要谓词的位置,建议优先使用上述宏。
查询结果的绘制方式
ClickStack 会根据列类型,自动将查询结果中的列映射到图表元素。映射规则因图表类型而异。
折线图和堆叠条形图
| 角色 | 列类型 | 说明 |
|---|---|---|
| 时间戳 | 第一个 Date 或 DateTime 列 |
用作 x 轴。 |
| 系列值 | 所有数值列 | 每个数值列都会绘制为单独的 series。这些通常是聚合值。 |
| 分组名称 | String、Map 或 Array 列 | 可选。分组值不同的行会绘制为单独的 series。 |
饼图
| 角色 | 列类型 | 描述 |
|---|---|---|
| 切片值 | 第一个数值列 | 决定每个切片的大小。 |
| 切片标签 | String、Map 或 Array 列 | 可选。每个唯一值都会成为一个切片标签。 |
数值图表
| 角色 | 列类型 | 描述 |
|---|---|---|
| 数值 | 第一个数值列 | 显示第一个数值列第一行的值。 |
表格图表
所有结果列都会直接显示为表格中的列。
示例
折线图 — 按服务划分的日志计数随时间变化
此查询按服务统计日志事件数,并按与仪表板粒度一致的时间间隔进行分组。
SELECT
toStartOfInterval(TimestampTime, INTERVAL {intervalSeconds:Int64} second) AS ts,
ServiceName,
count() AS count
FROM otel_logs
WHERE TimestampTime >= fromUnixTimestamp64Milli({startDateMilliseconds:Int64})
AND TimestampTime < fromUnixTimestamp64Milli({endDateMilliseconds:Int64})
AND $__filters
GROUP BY ServiceName, ts
ORDER BY ts ASCts(DateTime) 用作 x 轴时间戳。count(numeric) 作为序列值进行绘制。ServiceName(string) 为每个服务生成一条单独的折线。
折线图 — 使用宏
为简洁起见,以下是使用宏编写的同一查询:
SELECT
$__timeInterval(TimestampTime) AS ts,
ServiceName,
count() AS count
FROM otel_logs
WHERE $__timeFilter(TimestampTime)
AND $__filters
GROUP BY ServiceName, ts
ORDER BY ts ASC折线图 — 使用仪表板变量
假设仪表板中有一个基于 ServiceName 的 service 变量,以及一个基于 SeverityText 的 severity 变量,此查询会将图表范围限定为当前选定的服务,并排除选定的严重性级别。当变量未选择任何值时,两个宏都会展开为 1=1,因此在查看者选择值之前,图表不会应用过滤器。
SELECT
$__timeInterval(TimestampTime) AS ts,
count() AS count
FROM otel_logs
WHERE $__timeFilter(TimestampTime)
AND $__filter(ServiceName, $service)
AND $__conditionalAll(SeverityText NOT IN ($severity), $severity)
GROUP BY ts
ORDER BY ts ASC堆叠条形图 — 按严重级别统计的错误数
SELECT
$__timeInterval(TimestampTime) AS ts,
lower(SeverityText),
count() AS count
FROM otel_logs
WHERE $__timeFilter(TimestampTime)
AND lower(SeverityText) IN ('error', 'warn')
AND $__filters
GROUP BY SeverityText, ts
ORDER BY ts ASC表格图表 — 最慢的 10 个端点
SELECT
SpanName AS endpoint,
avg(Duration) / 1000 AS avg_duration_ms,
count() AS request_count
FROM otel_traces
WHERE $__timeFilter(Timestamp)
AND $__filters
GROUP BY SpanName
ORDER BY avg_duration_ms DESC
LIMIT 10饼图——按服务统计的请求分布
SELECT
ServiceName,
count() AS request_count
FROM otel_traces
WHERE $__timeFilter(Timestamp)
AND $__filters
GROUP BY ServiceNamerequest_count(数值) 决定每个扇区的大小。ServiceName(字符串) 用于标注每个扇区。
数值图表——错误总数
SELECT
count() AS total_errors
FROM otel_logs
WHERE $__timeFilter(TimestampTime)
AND SeverityText = 'error'
AND $__filters将显示第一行中的数值 total_errors。
注意事项
- 基于 SQL 的可视化会在启用
readonly模式的情况下执行——仅允许SELECT查询。 - 基于 SQL 的可视化必须且只能包含一条 SQL 查询,不支持多条查询。
- SQL 编辑器会为查询参数和宏提供自动补全建议。
- 必须先选择一个 source,才能将 仪表板 过滤器应用到基于 SQL 的可视化中。该 source 应与所查询的表一致,以确保过滤准确。
- 仪表板变量无需 source——其值来自仪表板的过滤器下拉列表,而非卡片自身的 source。
- 引用了仪表板变量的 SQL 卡片上的告警,会在所有变量均为空状态时进行评估,而不会使用仪表板中选定的值。