SigNoz stores all trace data in ClickHouse. When the visual Query Builder does not cover your use case, you can write ClickHouse SQL directly to build custom dashboard panels.
ClickHouse queries are only supported in Dashboards. To write a ClickHouse query, go to Dashboards, create a new dashboard or open an existing one, add a new panel, and select the ClickHouse Query tab.

Traces Schema
SigNoz uses several ClickHouse tables to store trace data. Understanding which table to query — and how they relate to each other — is key to writing efficient queries.
Main Spans Table — distributed_signoz_index_v3
This is the primary table you will query. It stores every span and has over 30 columns optimized for fast filtering on common attributes following OpenTelemetry Trace Semantic Conventions.
Full schema
(
`ts_bucket_start` UInt64 CODEC(DoubleDelta, LZ4),
`resource_fingerprint` String CODEC(ZSTD(1)),
`timestamp` DateTime64(9) CODEC(DoubleDelta, LZ4),
`trace_id` FixedString(32) CODEC(ZSTD(1)),
`span_id` String CODEC(ZSTD(1)),
`trace_state` String CODEC(ZSTD(1)),
`parent_span_id` String CODEC(ZSTD(1)),
`flags` UInt32 CODEC(T64, ZSTD(1)),
`name` LowCardinality(String) CODEC(ZSTD(1)),
`kind` Int8 CODEC(T64, ZSTD(1)),
`kind_string` String CODEC(ZSTD(1)),
`duration_nano` UInt64 CODEC(T64, ZSTD(1)),
`status_code` Int16 CODEC(T64, ZSTD(1)),
`status_message` String CODEC(ZSTD(1)),
`status_code_string` String CODEC(ZSTD(1)),
`attributes_string` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
`attributes_number` Map(LowCardinality(String), Float64) CODEC(ZSTD(1)),
`attributes_bool` Map(LowCardinality(String), Bool) CODEC(ZSTD(1)),
`resources_string` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
`resource` JSON(max_dynamic_paths = 100) CODEC(ZSTD(1)),
`events` Array(String) CODEC(ZSTD(2)),
`links` String CODEC(ZSTD(1)),
`response_status_code` LowCardinality(String) CODEC(ZSTD(1)),
`external_http_url` LowCardinality(String) CODEC(ZSTD(1)),
`http_url` LowCardinality(String) CODEC(ZSTD(1)),
`external_http_method` LowCardinality(String) CODEC(ZSTD(1)),
`http_method` LowCardinality(String) CODEC(ZSTD(1)),
`http_host` LowCardinality(String) CODEC(ZSTD(1)),
`db_name` LowCardinality(String) CODEC(ZSTD(1)),
`db_operation` LowCardinality(String) CODEC(ZSTD(1)),
`has_error` Bool CODEC(T64, ZSTD(1)),
`is_remote` LowCardinality(String) CODEC(ZSTD(1)),
`resource_string_service$$name` LowCardinality(String) DEFAULT resources_string['service.name'] CODEC(ZSTD(1)),
`attribute_string_http$$route` LowCardinality(String) DEFAULT attributes_string['http.route'] CODEC(ZSTD(1)),
`attribute_string_messaging$$system` LowCardinality(String) DEFAULT attributes_string['messaging.system'] CODEC(ZSTD(1)),
`attribute_string_messaging$$operation` LowCardinality(String) DEFAULT attributes_string['messaging.operation'] CODEC(ZSTD(1)),
`attribute_string_db$$system` LowCardinality(String) DEFAULT attributes_string['db.system'] CODEC(ZSTD(1)),
`attribute_string_rpc$$system` LowCardinality(String) DEFAULT attributes_string['rpc.system'] CODEC(ZSTD(1)),
`attribute_string_rpc$$service` LowCardinality(String) DEFAULT attributes_string['rpc.service'] CODEC(ZSTD(1)),
`attribute_string_rpc$$method` LowCardinality(String) DEFAULT attributes_string['rpc.method'] CODEC(ZSTD(1)),
`attribute_string_peer$$service` LowCardinality(String) DEFAULT attributes_string['peer.service'] CODEC(ZSTD(1))
)
ORDER BY (ts_bucket_start, resource_fingerprint, has_error, name, timestamp)
Column Reference
Core span columns:
| Column | Description |
|---|---|
timestamp | Time when the span was generated at the source |
trace_id | Unique trace identifier (W3C Trace Context) |
span_id | Unique span identifier |
parent_span_id | Span ID of the parent span (empty for root spans) |
trace_state | Trace state of the span |
flags | Span flags |
name | Operation name of the span (e.g., HTTP GET /api/users) |
kind / kind_string | Span kind as integer or string (Client, Server, Internal, Producer, Consumer) |
duration_nano | Span duration in nanoseconds. Divide by 1000000 to get milliseconds |
status_code / status_code_string | Span status code as integer or string |
status_message | Status message set on the span |
has_error | true if the span recorded an error |
events | Array of stringified JSON span events |
links | Stringified JSON span links |
Attribute maps — these store arbitrary key-value pairs from your instrumentation:
| Column | Description |
|---|---|
attributes_string | Map of all string span attributes |
attributes_number | Map of all numeric span attributes |
attributes_bool | Map of all boolean span attributes |
resource | JSON column containing all resource attributes (preferred) |
resources_string | Deprecated. Map of resource attributes — use the resource JSON column instead |
Pre-extracted columns — common attributes promoted to dedicated columns for faster filtering:
| Column | Derived from |
|---|---|
response_status_code | http.status_code, rpc.grpc.status_code, or rpc.jsonrpc.error_code |
http_url / external_http_url | HTTP URL of the span |
http_method / external_http_method | HTTP method of the span |
http_host | HTTP host of the span |
db_name / db_operation | db.name and db.operation attributes |
is_remote | Whether the span is remote |
resource_string_service$$name | service.name resource attribute |
Indexed attribute columns — attributes promoted to dedicated columns by SigNoz (the $$ replaces dots in the original attribute name):
| Column | Derived from |
|---|---|
attribute_string_http$$route | attributes_string['http.route'] |
attribute_string_messaging$$system | attributes_string['messaging.system'] |
attribute_string_messaging$$operation | attributes_string['messaging.operation'] |
attribute_string_db$$system | attributes_string['db.system'] |
attribute_string_rpc$$system | attributes_string['rpc.system'] |
attribute_string_rpc$$service | attributes_string['rpc.service'] |
attribute_string_rpc$$method | attributes_string['rpc.method'] |
attribute_string_peer$$service | attributes_string['peer.service'] |
Internal columns — used for query optimization:
| Column | Description |
|---|---|
ts_bucket_start | Timestamp rounded to a 30-minute bucket. Used for partition pruning — see Timestamp Bucketing |
resource_fingerprint | Hash of all resource attributes. Used with the resource filter table — see Resource Attribute Filtering |
Resource Filter Table — distributed_traces_v3_resource
This companion table maps resource attribute values to fingerprints. It is used in CTEs (Common Table Expressions) to dramatically speed up queries that filter by resource attributes like service.name or environment.
(
`labels` String CODEC(ZSTD(5)),
`fingerprint` String CODEC(ZSTD(1)),
`seen_at_ts_bucket_start` Int64 CODEC(Delta(8), ZSTD(1))
)
| Column | Description |
|---|---|
labels | JSON string containing all resource attributes for this fingerprint |
fingerprint | Unique identifier for this combination of resource attributes |
seen_at_ts_bucket_start | Last time bucket in which this fingerprint was observed |
You do not query this table directly in your final results. Instead, you use it in a WITH clause to find fingerprints matching your resource filters, then join those fingerprints into your main query. See Resource Attribute Filtering for the full pattern.
Error Events Table — distributed_signoz_error_index_v2
This table stores error events extracted from spans. Use it when you need to query exception details like type, message, and stacktrace.
Full schema
(
`timestamp` DateTime64(9) CODEC(DoubleDelta, LZ4),
`errorID` FixedString(32) CODEC(ZSTD(1)),
`groupID` FixedString(32) CODEC(ZSTD(1)),
`traceID` FixedString(32) CODEC(ZSTD(1)),
`spanID` String CODEC(ZSTD(1)),
`serviceName` LowCardinality(String) CODEC(ZSTD(1)),
`exceptionType` LowCardinality(String) CODEC(ZSTD(1)),
`exceptionMessage` String CODEC(ZSTD(1)),
`exceptionStacktrace` String CODEC(ZSTD(1)),
`exceptionEscaped` Bool CODEC(T64, ZSTD(1)),
`resourceTagsMap` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
INDEX idx_error_id errorID TYPE bloom_filter GRANULARITY 4,
INDEX idx_resourceTagsMapKeys mapKeys(resourceTagsMap) TYPE bloom_filter(0.01) GRANULARITY 64,
INDEX idx_resourceTagsMapValues mapValues(resourceTagsMap) TYPE bloom_filter(0.01) GRANULARITY 64
)
| Column | Description |
|---|---|
timestamp | Time when the error occurred |
errorID | Unique error identifier |
groupID | Groups related errors together |
traceID / spanID | Links back to the originating trace and span |
serviceName | Service that produced the error (from service.name) |
exceptionType | Exception class name (from exception.type) |
exceptionMessage | Error message (from exception.message) |
exceptionStacktrace | Full stacktrace (from exception.stacktrace) |
exceptionEscaped | Whether the exception escaped the span scope (from exception.escaped) |
resourceTagsMap | Map of resource attributes |
Lookup Tables
These smaller tables are useful for discovering available values and building dynamic filters.
distributed_top_level_operations — lists every service and its top-level operation names:
| Column | Description |
|---|---|
name | Operation name |
serviceName | Service name |
distributed_span_attributes_keys — lists every attribute key seen across all spans:
| Column | Description |
|---|---|
tagKey | Attribute name |
tagType | tag (span attribute) or resource (resource attribute) |
dataType | string, bool, or float64 |
isColumn | true if this attribute has been promoted to a selected (indexed) column |
distributed_span_attributes — stores attribute values, useful for exploring what values exist for a given key:
| Column | Description |
|---|---|
timestamp | When the attribute was seen |
tagKey | Attribute name |
tagType | tag or resource |
dataType | string, bool, or float64 |
stringTagValue | Value if the attribute is a string |
float64TagValue | Value if the attribute is numeric |
isColumn | Whether this attribute is a selected (indexed) column |
Essential Query Patterns
Every ClickHouse traces query uses a few common patterns. Understanding these patterns will help you write correct and performant queries.
Timestamp Bucketing
The ts_bucket_start column stores timestamps rounded to 30-minute buckets. ClickHouse uses this column to skip irrelevant data partitions, which significantly speeds up queries. You should include a ts_bucket_start filter in every query.
The pattern: if your time range is $start_datetime to $end_datetime, add:
ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
The - 1800 (30 minutes in seconds) ensures you do not miss spans that started in an earlier bucket but fall within your time range.
SigNoz dashboards provide the variables $start_datetime, $end_datetime, $start_timestamp, and $end_timestamp automatically. Use them in your queries instead of hardcoding times.
Resource Attribute Filtering
Filtering by resource attributes (like service.name, environment, or k8s.namespace.name) is one of the most effective ways to speed up your queries. Instead of scanning the entire spans table, ClickHouse first looks up matching fingerprints in the much smaller distributed_traces_v3_resource table, then uses those fingerprints to narrow the scan on the main table.
Use a Common Table Expression (CTE) to apply resource filters:
-- Step 1: Find fingerprints matching your resource filters
WITH __resource_filter AS (
SELECT fingerprint
FROM signoz_traces.distributed_traces_v3_resource
WHERE (simpleJSONExtractString(labels, 'service.name') = 'my-service')
AND seen_at_ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
)
-- Step 2: Use those fingerprints in your main query
SELECT count(*)
FROM signoz_traces.distributed_signoz_index_v3
WHERE resource_fingerprint GLOBAL IN __resource_filter
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
AND timestamp >= $start_datetime AND timestamp <= $end_datetime
You can filter on multiple resource attributes by adding more conditions to the WHERE clause of the CTE:
WITH __resource_filter AS (
SELECT fingerprint
FROM signoz_traces.distributed_traces_v3_resource
WHERE (simpleJSONExtractString(labels, 'service.name') = 'frontend')
AND (simpleJSONExtractString(labels, 'environment') = 'production')
AND seen_at_ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
)
Always include resource attribute filters in your queries when possible. This is one of the most effective ways to optimize trace query performance. Read more about this in the query performance improvement blog post.
Using Resource Attributes in SELECT and GROUP BY
To include resource attributes in your SELECT clause or GROUP BY statements, use the format: resource.<keyname>::String
For example: resource.service.name::String
Querying Span Attributes
How you reference an attribute depends on whether it is a standard (non-indexed) attribute or a selected (indexed) attribute.
Standard attributes — use the map column with bracket syntax:
-- String attribute
attributes_string['http.status_code']
-- Number attribute
attributes_number['response_time']
-- Boolean attribute
attributes_bool['is_cached']
Selected (indexed) attributes — when an attribute is promoted to a selected field, SigNoz creates a dedicated column for it. Use the column name directly for better performance:
-- Format: attribute_<dataType>_<key_with_dollars>
-- Dots in the attribute name are replaced with $$
attribute_string_http$$route -- equivalent to attributes_string['http.route']
attribute_number_response$$time -- equivalent to attributes_number['response.time']
When an attribute is selected, SigNoz also creates an _exists column (e.g., attribute_string_http$$method_exists) that you can use to check whether the attribute is present on a span.
Dashboard Panel Examples
SigNoz dashboards support three panel types: Timeseries, Value, and Table. Below are examples for each.
Timeseries Panels
Timeseries panels plot aggregated data over time. Your query must return a timestamp column and one or more value columns.
Count of spans per 100ms interval
Count spans from the frontend service with duration over 50ms, bucketed into 100ms intervals:
WITH __resource_filter AS (
SELECT fingerprint
FROM signoz_traces.distributed_traces_v3_resource
WHERE (simpleJSONExtractString(labels, 'service.name') = 'frontend')
AND seen_at_ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
)
SELECT
fromUnixTimestamp64Milli(intDiv(toUnixTimestamp64Milli(timestamp), 100) * 100) AS interval,
toFloat64(count()) AS count
FROM signoz_traces.distributed_signoz_index_v3
WHERE resource_fingerprint GLOBAL IN __resource_filter
AND duration_nano >= 50 * exp10(6)
AND timestamp BETWEEN $start_datetime AND $end_datetime
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
GROUP BY interval
ORDER BY interval ASC;
Spans per minute per service
WITH __resource_filter AS (
SELECT fingerprint
FROM signoz_traces.distributed_traces_v3_resource
WHERE seen_at_ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
)
SELECT
toStartOfInterval(timestamp, INTERVAL 1 MINUTE) AS ts,
resource.service.name::String as `service.name`,
toFloat64(count()) AS value
FROM signoz_traces.distributed_signoz_index_v3
WHERE resource_fingerprint GLOBAL IN __resource_filter
AND timestamp BETWEEN $start_datetime AND $end_datetime
AND `service.name` IS NOT NULL
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
GROUP BY `service.name`, ts
ORDER BY ts ASC;
Average duration by HTTP route over time
WITH __resource_filter AS (
SELECT fingerprint
FROM signoz_traces.distributed_traces_v3_resource
WHERE seen_at_ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
)
SELECT
toStartOfInterval(timestamp, INTERVAL 1 MINUTE) AS interval,
attributes_string['http.route'] AS route,
toFloat64(avg(duration_nano)) AS value
FROM signoz_traces.distributed_signoz_index_v3
WHERE resource_fingerprint GLOBAL IN __resource_filter
AND attributes_string['http.method'] != ''
AND timestamp BETWEEN $start_datetime AND $end_datetime
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
GROUP BY route, interval
ORDER BY route, interval ASC;
Value Panels
Value panels reduce a query result to a single number. You can apply aggregate functions like avg, sum, min, or max.
Average span duration for POST requests
Average duration in milliseconds for POST requests to the frontend service:
WITH __resource_filter AS (
SELECT fingerprint
FROM signoz_traces.distributed_traces_v3_resource
WHERE (simpleJSONExtractString(labels, 'service.name') = 'frontend')
AND seen_at_ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
)
SELECT
avg(duration_nano) / 1000000 as duration_ms
FROM signoz_traces.distributed_signoz_index_v3
WHERE timestamp BETWEEN $start_datetime AND $end_datetime
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
AND http_method = 'POST'
AND resource_fingerprint GLOBAL IN __resource_filter;
Average span count per minute
Average number of spans per minute for the frontend service in production:
WITH __resource_filter AS (
SELECT fingerprint
FROM signoz_traces.distributed_traces_v3_resource
WHERE (simpleJSONExtractString(labels, 'service.name') = 'frontend')
AND (simpleJSONExtractString(labels, 'environment') = 'production')
AND seen_at_ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
)
SELECT avg(value) as avg_count FROM (
SELECT
toStartOfInterval(timestamp, INTERVAL 1 MINUTE) AS ts,
toFloat64(count()) AS value
FROM signoz_traces.distributed_signoz_index_v3
WHERE resource_fingerprint GLOBAL IN __resource_filter
AND timestamp BETWEEN $start_datetime AND $end_datetime
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
GROUP BY ts
ORDER BY ts ASC
);
Table Panels
Table panels display data in tabular format. Queries are similar to timeseries queries but are typically grouped by a dimension rather than time.
Average duration by span attribute
Group spans by a custom attribute and show the average duration for each value:
WITH __resource_filter AS (
SELECT fingerprint
FROM signoz_traces.distributed_traces_v3_resource
WHERE seen_at_ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
)
SELECT
attributes_string['example_string_attribute'] AS attribute_name,
toFloat64(avg(duration_nano)) AS value
FROM signoz_traces.distributed_signoz_index_v3
WHERE resource_fingerprint GLOBAL IN __resource_filter
AND timestamp BETWEEN $start_datetime AND $end_datetime
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
GROUP BY attribute_name
ORDER BY attribute_name ASC;
Span count by service
WITH __resource_filter AS (
SELECT fingerprint
FROM signoz_traces.distributed_traces_v3_resource
WHERE seen_at_ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
)
SELECT
resource.service.name::String as `service.name`,
toFloat64(count()) AS value
FROM signoz_traces.distributed_signoz_index_v3
WHERE resource_fingerprint GLOBAL IN __resource_filter
AND timestamp BETWEEN $start_datetime AND $end_datetime
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
AND `service.name` IS NOT NULL
GROUP BY `service.name`
ORDER BY value DESC;
Average duration by HTTP method for a specific service
WITH __resource_filter AS (
SELECT fingerprint
FROM signoz_traces.distributed_traces_v3_resource
WHERE (simpleJSONExtractString(labels, 'service.name') = 'api-gateway')
AND seen_at_ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
)
SELECT
http_method,
toFloat64(avg(duration_nano)) AS avg_duration_nano
FROM signoz_traces.distributed_signoz_index_v3
WHERE resource_fingerprint GLOBAL IN __resource_filter
AND timestamp BETWEEN $start_datetime AND $end_datetime
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
AND http_method IS NOT NULL AND http_method != ''
GROUP BY http_method
ORDER BY avg_duration_nano DESC;
Advanced Examples
Querying for Specific Service Latencies
The Traces Explorer list shows root span duration, but to measure the actual time a specific service call takes, you need to filter for Client spans — these represent outbound calls from one service to another.
Understanding Span Kinds
| Kind | Meaning | When to use |
|---|---|---|
Client | Outbound call to another service | Measuring service-to-service call latency |
Server | Handling an incoming request | Measuring how long a service takes to process a request |
Internal | Internal operation within a service | Measuring internal processing time |
Producer / Consumer | Message queue send/receive | Measuring messaging latency |
Measuring Cart Service latency from Checkout Service
This query measures p95, p99, and max latency for calls from the Checkout Service to the Cart Service:
WITH __resource_filter AS (
SELECT fingerprint
FROM signoz_traces.distributed_traces_v3_resource
WHERE (simpleJSONExtractString(labels, 'service.name') = 'checkout-service-production')
AND seen_at_ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
)
SELECT
toStartOfInterval(timestamp, INTERVAL 1 MINUTE) AS ts,
quantile(0.95)(duration_nano / 1000000) AS p95_latency_ms,
quantile(0.99)(duration_nano / 1000000) AS p99_latency_ms,
max(duration_nano / 1000000) AS max_latency_ms,
toFloat64(count()) AS call_count
FROM signoz_traces.distributed_signoz_index_v3
WHERE resource_fingerprint GLOBAL IN __resource_filter
AND timestamp BETWEEN {{.start_datetime}} AND {{.end_datetime}}
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
AND kind_string = 'Client'
AND http_url LIKE '%cart-service.prod%'
GROUP BY ts
ORDER BY ts ASC;
Key points:
- The resource filter targets the calling service (
checkout-service-production), not the called service. kind_string = 'Client'selects only outbound call spans.- The URL filter matches the target service endpoint.
Error spans per service
WITH __resource_filter AS (
SELECT fingerprint
FROM signoz_traces.distributed_traces_v3_resource
WHERE seen_at_ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
)
SELECT
resource.service.name::String as `service.name`,
toFloat64(count()) AS value
FROM signoz_traces.distributed_signoz_index_v3
WHERE resource_fingerprint GLOBAL IN __resource_filter
AND timestamp BETWEEN $start_datetime AND $end_datetime
AND has_error = true
AND `service.name` IS NOT NULL
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
GROUP BY `service.name`;
Average latency between two spans in a trace
This advanced query measures the time difference between two specific spans within the same trace — useful for measuring latency between two services that are not direct parent-child relationships.
This example measures the average time between the driver service's /driver.DriverService/FindNearest span and the route service's HTTP GET /route span:
SELECT
interval,
round(avg(time_diff), 2) AS result
FROM
(
SELECT
interval,
traceID,
if(startTime1 != 0,
if(startTime2 != 0,
(toUnixTimestamp64Nano(startTime2) - toUnixTimestamp64Nano(startTime1)) / 1000000,
nan),
nan) AS time_diff
FROM
(
SELECT
toStartOfInterval(timestamp, toIntervalMinute(1)) AS interval,
traceID,
minIf(timestamp,
resource_string_service$$name = 'driver'
AND name = '/driver.DriverService/FindNearest'
) AS startTime1,
minIf(timestamp,
resource_string_service$$name = 'route'
AND name = 'HTTP GET /route'
) AS startTime2
FROM signoz_traces.distributed_signoz_index_v3
WHERE timestamp BETWEEN $start_datetime AND $end_datetime
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
AND resource_string_service$$name IN ('driver', 'route')
GROUP BY interval, traceID
ORDER BY interval, traceID ASC
)
)
WHERE isNaN(time_diff) = 0
GROUP BY interval
ORDER BY interval ASC;
Querying span events
Span events are stored as a JSON array in the events column. You can extract data from them using ClickHouse's array and JSON functions.
Count of each customer_id from a span event
This query extracts the customer_id attribute from span events named Getting customer and counts how many times each value appears:
WITH arrayFilter(
x -> JSONExtractString(x, 'name') = 'Getting customer', events
) AS filteredEvents
SELECT
toFloat64(count()) AS count,
arrayJoin(arrayMap(
x -> JSONExtractString(JSONExtractString(x, 'attributeMap'), 'customer_id'),
filteredEvents
)) AS resultArray
FROM signoz_traces.distributed_signoz_index_v3
WHERE not empty(filteredEvents)
AND timestamp BETWEEN $start_datetime AND $end_datetime
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
GROUP BY resultArray
ORDER BY resultArray ASC;
Sum of customer_id values from a span event over time
WITH arrayFilter(
x -> JSONExtractString(x, 'name') = 'Getting customer', events
) AS filteredEvents
SELECT
toStartOfInterval(timestamp, INTERVAL 1 MINUTE) AS interval,
toFloat64(sum(toInt32(resultArray))) AS sum,
arrayJoin(arrayMap(
x -> JSONExtractString(JSONExtractString(x, 'attributeMap'), 'customer_id'),
filteredEvents
)) AS resultArray
FROM signoz_traces.distributed_signoz_index_v3
WHERE not empty(filteredEvents)
AND timestamp BETWEEN $start_datetime AND $end_datetime
AND ts_bucket_start BETWEEN $start_timestamp - 1800 AND $end_timestamp
GROUP BY resultArray, interval
ORDER BY resultArray, interval ASC;