SigNoz
Docs
PricingCustomers
Get Started - Free
Docs
IntroductionContributingMigrate from DatadogSigNoz API
OpenTelemetry
What is OpenTelemetryOpenTelemetry Collector GuideOpenTelemetry Demo
Community
Support
Slack
X
Launch Week
Changelog
Dashboard Templates
DevOps Wordle
Newsletter
KubeCon, Atlanta 2025
More
SigNoz vs DatadogSigNoz vs New RelicSigNoz vs GrafanaSigNoz vs Dynatrace
Careers
AboutTermsPrivacySecurity & Compliance
SigNoz Logo
SigNoz
All systems operational
HIPAASOC-2
SigNoz Cloud - This page applies to SigNoz Cloud editions.
Self-Host - This page applies to self-hosted SigNoz editions.

Traces Schema - Write ClickHouse Queries & Build Dashboard Panels

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.

ClickHouse Query editor in SigNoz Dashboards
ClickHouse Query editor in SigNoz Dashboards

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:

ColumnDescription
timestampTime when the span was generated at the source
trace_idUnique trace identifier (W3C Trace Context)
span_idUnique span identifier
parent_span_idSpan ID of the parent span (empty for root spans)
trace_stateTrace state of the span
flagsSpan flags
nameOperation name of the span (e.g., HTTP GET /api/users)
kind / kind_stringSpan kind as integer or string (Client, Server, Internal, Producer, Consumer)
duration_nanoSpan duration in nanoseconds. Divide by 1000000 to get milliseconds
status_code / status_code_stringSpan status code as integer or string
status_messageStatus message set on the span
has_errortrue if the span recorded an error
eventsArray of stringified JSON span events
linksStringified JSON span links

Attribute maps — these store arbitrary key-value pairs from your instrumentation:

ColumnDescription
attributes_stringMap of all string span attributes
attributes_numberMap of all numeric span attributes
attributes_boolMap of all boolean span attributes
resourceJSON column containing all resource attributes (preferred)
resources_stringDeprecated. Map of resource attributes — use the resource JSON column instead

Pre-extracted columns — common attributes promoted to dedicated columns for faster filtering:

ColumnDerived from
response_status_codehttp.status_code, rpc.grpc.status_code, or rpc.jsonrpc.error_code
http_url / external_http_urlHTTP URL of the span
http_method / external_http_methodHTTP method of the span
http_hostHTTP host of the span
db_name / db_operationdb.name and db.operation attributes
is_remoteWhether the span is remote
resource_string_service$$nameservice.name resource attribute

Indexed attribute columns — attributes promoted to dedicated columns by SigNoz (the $$ replaces dots in the original attribute name):

ColumnDerived from
attribute_string_http$$routeattributes_string['http.route']
attribute_string_messaging$$systemattributes_string['messaging.system']
attribute_string_messaging$$operationattributes_string['messaging.operation']
attribute_string_db$$systemattributes_string['db.system']
attribute_string_rpc$$systemattributes_string['rpc.system']
attribute_string_rpc$$serviceattributes_string['rpc.service']
attribute_string_rpc$$methodattributes_string['rpc.method']
attribute_string_peer$$serviceattributes_string['peer.service']

Internal columns — used for query optimization:

ColumnDescription
ts_bucket_startTimestamp rounded to a 30-minute bucket. Used for partition pruning — see Timestamp Bucketing
resource_fingerprintHash 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))
)
ColumnDescription
labelsJSON string containing all resource attributes for this fingerprint
fingerprintUnique identifier for this combination of resource attributes
seen_at_ts_bucket_startLast 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
)
ColumnDescription
timestampTime when the error occurred
errorIDUnique error identifier
groupIDGroups related errors together
traceID / spanIDLinks back to the originating trace and span
serviceNameService that produced the error (from service.name)
exceptionTypeException class name (from exception.type)
exceptionMessageError message (from exception.message)
exceptionStacktraceFull stacktrace (from exception.stacktrace)
exceptionEscapedWhether the exception escaped the span scope (from exception.escaped)
resourceTagsMapMap 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:

ColumnDescription
nameOperation name
serviceNameService name

distributed_span_attributes_keys — lists every attribute key seen across all spans:

ColumnDescription
tagKeyAttribute name
tagTypetag (span attribute) or resource (resource attribute)
dataTypestring, bool, or float64
isColumntrue 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:

ColumnDescription
timestampWhen the attribute was seen
tagKeyAttribute name
tagTypetag or resource
dataTypestring, bool, or float64
stringTagValueValue if the attribute is a string
float64TagValueValue if the attribute is numeric
isColumnWhether 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

KindMeaningWhen to use
ClientOutbound call to another serviceMeasuring service-to-service call latency
ServerHandling an incoming requestMeasuring how long a service takes to process a request
InternalInternal operation within a serviceMeasuring internal processing time
Producer / ConsumerMessage queue send/receiveMeasuring 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;

Last updated: May 18, 2026

Edit on GitHub

Was this page helpful?

Your response helps us improve this page.

Prev
Logs
Next
PromQL
On this page
Traces Schema
Main Spans Table — `distributed_signoz_index_v3`
Resource Filter Table — `distributed_traces_v3_resource`
Error Events Table — `distributed_signoz_error_index_v2`
Lookup Tables
Essential Query Patterns
Timestamp Bucketing
Resource Attribute Filtering
Querying Span Attributes
Dashboard Panel Examples
Timeseries Panels
Value Panels
Table Panels
Advanced Examples
Querying for Specific Service Latencies
Error spans per service
Average latency between two spans in a trace
Querying span events

Is this page helpful?

Your response helps us improve this page.