Table schemas and enum types
This section contains subsets of the table schemas and enumerated types (enums) that are relevant to the SQL Editor.spans
Column | Type | Example value |
---|---|---|
span_id | UUID | "00000000-0000-0000-1234-426614174000" |
status | String | "error" |
name | String | "openai.chat" |
path | String | "workflow.process.step1.openai.chat" |
trace_id | UUID | "12345678-90ab-cdef-1234-426614174000" |
parent_span_id | UUID | "00000000-0000-0000-a456-abcd5667ef09" |
span_type | UInt8 | 1 |
start_time | DateTime64 | "2021-01-01 00:00:00+00" |
end_time | DateTime64 | "2021-01-01 00:00:00+00" |
input | String | "[{\"role\": \"user\", \"content\": \"Hello, world!\"}]" |
output | String | "[{\"role\": \"assistant\", \"content\": \"Hi! How can I help you today?\"}]" |
attributes | String | "{\"gen_ai.system\": \"openai\", \"gen_ai.model\": \"gpt-4o\"}" |
request_model | String | "gpt-4.1-mini" |
response_model | String | "gpt-4.1-mini-2025-04-14" |
model | String | "gpt-4.1-mini-2025-04-14" |
provider | String | "openai" |
input_tokens | UInt64 | 150 |
output_tokens | UInt64 | 100 |
total_tokens | UInt64 | 250 |
total_cost | Float64 | 0.6897 |
input_cost | Float64 | 0.5667 |
output_cost | Float64 | 0.123 |
Path
Laminar span path is stored as an array of span names in span attributes. However, in the SQL queries, it is stored as a string with items joined by a dot. For example, if the span path is["outer", "inner"]
, the path
column will be "outer.inner"
.
If needed, you can still access the array value by accessing the attributes
using simpleJSONExtractRaw(attributes, 'lmnr.span.path')
.
Parent span ID
If the current span is the top span of the trace, theparent_span_id
will be a 0 UUID, i.e. "00000000-0000-0000-0000-000000000000"
.
Span type
Here are the values of thespan_type
column and their meanings:
Input and output
Theinput
and output
columns are stored as either raw strings or stringified JSONs. The best way to parse them is to try
to parse them as JSON, and if it fails, use the raw string. You can also use isValidJSON
function right in the query to test for this.
input
and output
columns are also indexed on content, so you can use them in WHERE conditions. Use ILIKE
instead of LIKE
, because the index is case-insensitive.
Attributes
Theattributes
column is stored as a string in JSON format. That is, you can safely JSON.parse
/ json.loads
them. In addition,
you can use JSON* and simpleJSON* functions on them right in the queries. Attributes are guaranteed to be a valid JSON object.
Model
Themodel
column is set to the response model if present, otherwise it is set to the request model.
Total tokens and total cost
Usually, thetotal_tokens = input_tokens + output_tokens
and total_cost = input_cost + output_cost
.
However, you can manually report these values using the relevant attributes. In this case, totals may
not be equal to the sum of the input and output tokens and costs.
traces
Column | Type | Example value |
---|---|---|
trace_id | UUID | "01234567-1234-cdef-1234-426614174000" |
trace_type | UInt8 | 0 |
start_time | DateTime64 | "2021-01-01 00:00:00+00" |
end_time | DateTime64 | "2021-01-01 00:00:00+00" |
duration | Float64 | 1.23 |
input_tokens | UInt64 | 150 |
output_tokens | UInt64 | 100 |
total_tokens | UInt64 | 250 |
total_cost | Float64 | 0.6897 |
input_cost | Float64 | 0.5667 |
output_cost | Float64 | 0.123 |
status | String | "error" |
user_id | String | "user_123" |
session_id | String | "session_123" |
metadata | String | "{\"key\": \"value\"}" |
top_span_id | UUID | "00000000-0000-0000-1234-426614174000" |
Trace type
Here are the values of thetrace_type
column and their meanings:
Duration
The duration is in seconds, and is calculated asend_time - start_time
.
Status
Status is set to error if any of the spans in the trace have statuserror
. Empty status means success.
Metadata
Metadata is stored as a string in JSON format. That is, you can safelyJSON.parse
/ json.loads
it. In addition,
you can use JSON* and simpleJSON* functions on it right in the queries. Metadata is guaranteed to be a valid JSON object.
events
Column | Type | Example value |
---|---|---|
id | UUID | "01234567-89ab-4def-1234-426614174000" |
span_id | UUID | "00000000-0000-0000-1234-426614174000" |
name | String | "My custom event" |
timestamp | DateTime | "2021-01-01 00:00:00+00" |
attributes | String | "{\"key\": \"value\"}" |
user_id | String | "user_123" |
session_id | String | "session_123" |
Notes
Attributes Theattributes
column is stored as a string in JSON format. That is, you can safely JSON.parse
/ json.loads
it. In addition,
you can use JSON* and simpleJSON* functions on it right in the queries. Attributes are guaranteed to be a valid JSON object.
evaluation_datapoints
Column | Type | Example value |
---|---|---|
id | UUID | "01234567-89ab-4def-1234-426614174000" |
trace_id | UUID | "01234567-1234-cdef-1234-426614174000" |
evaluation_id | UUID | "98765432-1098-4654-3210-987654321098" |
created_at | DateTime64 | "2021-01-01 00:00:00+00" |
data | String | "{\"key\": \"value\"}" |
target | String | "{\"key\": \"value\"}" |
metadata | String | "{\"key\": \"value\"}" |
index | UInt64 | 0 |
evaluation_scores
Evaluation scores are stored in a separate table, linked to the evaluation datapoints. The scores are flattened into a single row per score. For example, if your evaluation has 3 scores,{ "score1": 0.85, "score2": 0.90, "score3": 0.95 }
will be stored as 3 rows in the evaluation_scores
table.
Column | Type | Example value |
---|---|---|
evaluation_id | UUID | "01234567-89ab-cdef-1234-426614174000" |
evaluation_datapoint_id | UUID | "98765432-1098-7654-3210-987654321098" |
timestamp | DateTime64 | "2021-01-01 00:00:00+00" |
name | String | "My custom score" |
value | Float64 | 0.85 |
metadata | String | "{\"key\": \"value\"}" |
trace_id | UUID | "01234567-89ab-cdef-1234-426614174000" |
Best practices
Avoid joins
ClickHouse is a columnar database, so, while JOINs are supported, they are not efficient. If you are joining tables with more than a few hundred rows, the query will likely timeout or fail.Solution
Query the data you need, and join the relevant data in your application.Add start_time filter
You almost certainly want to add astart_time
filter to your query.
Spans table (and thus traces
aggregation on it) is sorted by start_time
and trace_id
, so if you
apply a start_time
WHERE condition, the query will run faster.
Advantages:
- Queries run faster
- Queries will never fail because of running out of memory
Searching in span input or output
You can search in theinput
and output
columns of the spans
table.
The search is optimized to be case-insensitive, so use ILIKE
instead of LIKE
.