---
title: "SQL schema"
sidebar:
  label: "SQL schema"
description: "The tables and columns you can query with Judgment's virtual SQL (vsql)."
seo:
  title: "SQL schema | Judgment Docs"
  description: "The tables and columns you can query with Judgment's virtual SQL (vsql)."
---

Judgment's virtual SQL (vsql) is a fixed set of read-only tables over your project's data. You query them with `sql_query` over MCP, with the Judgment Agent, or with `client.sql()` in the SDK. For how to run queries and worked examples, see [Query with SQL](/documentation/agents/query-with-sql).

## Rules

| Rule | Value |
| --- | --- |
| Statements | One read-only `SELECT` per request. Writes and other statements are rejected. |
| Dialect | ClickHouse SQL functions and syntax. |
| Query text | 1 to 50,000 characters, with at least one non-whitespace character. |
| Result size | At most 1,000 rows and 5 MiB. Exceeding either cap returns an error. |
| Scope | The server applies your organization and project. SQL can't read another project. |
| Memory | Queries run with a 2 GiB memory limit. On `QUERY_MEMORY_LIMIT`, narrow the time range or select fewer columns. |
| Environments | `environment_name` is `''` for traces with no environment. Omit the filter to include all environments. |

`LIMIT` caps the rows returned, not the rows scanned. Filter on `started_at` or on IDs to keep queries fast.

The same reference is available as Markdown from MCP `discover_schema`, Python `client.discover_schema()`, TypeScript `client.discoverSchema()`, and `GET /v1/sql/schema`.

API and SQL names still say `behavior`; it means label. `config.behaviors` holds the labels each judge can apply, and `telemetry.scores.behavior_id_at_evaluation` records which label a judge applied. New judges are `categorical`; `binary` and `numeric` scores come from older judges.

`evaluations.runs` and `evaluations.items` hold test runs started from a test config over a dataset. Test suite runs aren't in these tables.

## Patterns

| You want | Do this |
| --- | --- |
| Sessions | There is no sessions table. Group `telemetry.traces` by `session_id`. |
| Cost and tokens | Sum `cost_usd` and token columns on `telemetry.spans` before joining to scores or tags, so rows aren't counted twice. |
| A dataset version `N` | `version_added <= N AND (version_removed IS NULL OR version_removed > N)` on `datasets.examples`. |
| Stable paging | Order by a unique tie-breaker, for example `ORDER BY started_at DESC, trace_id DESC`, and continue from the last row. |
| Large attributes | Read a window with `substringUTF8(attributes['key'], 1, 2000)` and get the full length with `lengthUTF8`. |

## Tables

{/* Generated by pnpm scripts:generate-virtual-sql-catalog; do not edit below. */}

Judgment SQL (telemetry.v1)

One read-only SELECT against these virtual tables; host supplies org/project scope; SQL cannot override it.

Memory: prefer narrow time ranges or trace/span IDs and only needed fields. Small LIMITs help inspection but do not bound scan, grouping, sort or join memory. Run broad scans sequentially. On QUERY_MEMORY_LIMIT, narrow scope or simplify before retrying.

### telemetry.traces
One latest root record per trace_id

| Column | Type | Description |
| --- | --- | --- |
| `trace_id` | `String` | Trace identifier |
| `root_span_id` | `String` | Root span identifier |
| `session_id` | `Nullable(String)` | Session identifier |
| `started_at` | `DateTime64(6)` | Root span start in UTC |
| `ended_at` | `Nullable(DateTime64(6))` | Root span end in UTC |
| `duration_ms` | `Nullable(Float64)` | Root duration in milliseconds |
| `trace_name` | `String` | Root span name |
| `status` | `String` | unset, ok, or error |
| `error_message` | `Nullable(String)` | Root error text |
| `input` | `Nullable(String)` | Root input |
| `output` | `Nullable(String)` | Root output |
| `customer_id` | `Nullable(String)` | Customer identifier |
| `customer_user_id` | `Nullable(String)` | Customer user identifier |
| `service_name` | `Nullable(String)` | OTEL service name |
| `agent_version` | `Nullable(String)` | Agent version |
| `attributes` | `Map(String, String)` | Span attributes |
| `resource_attributes` | `Map(String, String)` | OTEL resource attributes |
| `environment_name` | `String` | Root environment; '' = no environment |


### telemetry.spans
One latest record per (trace_id, span_id)

| Column | Type | Description |
| --- | --- | --- |
| `trace_id` | `String` | Trace identifier |
| `span_id` | `String` | Span identifier |
| `parent_span_id` | `Nullable(String)` | Parent span identifier |
| `session_id` | `Nullable(String)` | Session identifier |
| `started_at` | `DateTime64(6)` | Span start in UTC |
| `ended_at` | `Nullable(DateTime64(6))` | Span end in UTC |
| `duration_ms` | `Nullable(Float64)` | Duration in milliseconds |
| `span_name` | `String` | Span name |
| `span_kind` | `String` | OTEL span kind |
| `service_name` | `Nullable(String)` | OTEL service name |
| `status` | `String` | unset, ok, or error |
| `error_message` | `Nullable(String)` | Span error text |
| `input` | `Nullable(String)` | Span input |
| `output` | `Nullable(String)` | Span output |
| `customer_id` | `Nullable(String)` | Customer identifier |
| `customer_user_id` | `Nullable(String)` | Customer user identifier |
| `agent_version` | `Nullable(String)` | Agent version |
| `model` | `Nullable(String)` | Model name |
| `llm_provider` | `Nullable(String)` | LLM provider |
| `cost_usd` | `Float64` | Span cost; zero when unreported |
| `non_cached_input_tokens` | `UInt64` | Non-cached input tokens; zero when unreported |
| `cache_read_input_tokens` | `UInt64` | Cache-read input tokens; zero when unreported |
| `cache_creation_input_tokens` | `UInt64` | Cache-creation input tokens; zero when unreported |
| `output_tokens` | `UInt64` | Output tokens; zero when unreported |
| `attributes` | `Map(String, String)` | Span attributes |
| `resource_attributes` | `Map(String, String)` | OTEL resource attributes |
| `trace_state` | `Nullable(String)` | OTEL trace state |
| `link_target_trace_id` | `Nullable(String)` | Linked target trace identifier |
| `link_target_span_id` | `Nullable(String)` | Linked target span identifier |
| `link_source_trace_id` | `Nullable(String)` | Linked source trace identifier |
| `link_source_span_id` | `Nullable(String)` | Linked source span identifier |
| `events_json` | `String` | Ordered span events as JSON with Timestamp, Name, and Attributes; [] when empty |
| `environment_name` | `String` | Span environment; '' = no environment |


### telemetry.scores
One retained evaluation result

| Column | Type | Description |
| --- | --- | --- |
| `score_id` | `String` | Evaluation result identifier |
| `evaluated_at` | `DateTime64(6)` | Evaluation time in UTC |
| `evaluation_scope` | `String` | span, trace, session, or unknown |
| `trace_id` | `Nullable(String)` | Evaluated trace identifier |
| `span_id` | `Nullable(String)` | Associated span identifier; null when no span is recorded |
| `session_id` | `Nullable(String)` | Evaluated session identifier |
| `evaluation_trace_count` | `Nullable(UInt32)` | Number of traces included in a session evaluation; null when unreported |
| `judge_id` | `String` | Stable judge identifier |
| `judge_name_at_evaluation` | `String` | Historical judge name |
| `judge_type_at_evaluation` | `String` | Historical judge type |
| `judge_major_version` | `UInt32` | Historical major version |
| `judge_minor_version` | `UInt32` | Historical minor version |
| `score_type` | `String` | numeric, binary, or categorical |
| `numeric_value` | `Nullable(Float64)` | Numeric score |
| `boolean_value` | `Nullable(Bool)` | Binary score |
| `categorical_value` | `Nullable(String)` | Categorical score |
| `value_text` | `Nullable(String)` | Normalized score text |
| `behavior_id_at_evaluation` | `Nullable(String)` | Label identifier at evaluation time |
| `reason_json` | `Nullable(String)` | Evaluation reason JSON |
| `error_message` | `Nullable(String)` | Evaluation error |
| `customer_id` | `Nullable(String)` | Customer identifier |
| `customer_user_id` | `Nullable(String)` | Customer user identifier |
| `environment_name` | `String` | Source trace environment; '' = no environment |


### telemetry.trace_tags
One tag assignment per (trace_id, tag)

| Column | Type | Description |
| --- | --- | --- |
| `trace_id` | `String` | Tagged trace identifier |
| `tag` | `String` | Tag value |
| `created_at` | `DateTime64(6)` | Assignment time in UTC |


### telemetry.alerts
One alert generated by an automation rule

| Column | Type | Description |
| --- | --- | --- |
| `alert_id` | `String` | Alert identifier |
| `trace_id` | `String` | Triggering trace identifier |
| `rule_id` | `String` | Automation rule identifier |
| `rule_name` | `String` | Rule name at alert time |
| `created_at` | `DateTime64(6)` | Alert creation time in UTC |
| `combine_type` | `String` | Rule condition combination type |
| `notification_sent` | `Bool` | Whether notification delivery was claimed |
| `conditions_result_json` | `String` | Rule condition results as JSON |
| `metadata_json` | `String` | Alert metadata as JSON |
| `environment_name` | `String` | Source trace environment; '' = no environment |


### evaluations.runs
One current evaluation test run

| Column | Type | Description |
| --- | --- | --- |
| `run_id` | `String` | Evaluation run identifier |
| `test_config_id` | `String` | Test configuration identifier |
| `name` | `String` | Run name |
| `dataset_id` | `String` | Evaluated dataset identifier |
| `dataset_version_id` | `String` | Evaluated dataset version identifier |
| `status` | `String` | Current run status |
| `source` | `String` | Run initiation source |
| `created_at` | `DateTime64(6)` | Creation time in UTC |
| `started_at` | `Nullable(DateTime64(6))` | Start time in UTC |
| `completed_at` | `Nullable(DateTime64(6))` | Completion time in UTC |
| `error_message` | `Nullable(String)` | Run failure message |


### evaluations.items
One latest scorer result per run, example, judge, and judge version

| Column | Type | Description |
| --- | --- | --- |
| `run_id` | `String` | Evaluation run identifier |
| `dataset_id` | `String` | Evaluated dataset identifier |
| `dataset_version_id` | `String` | Evaluated dataset version identifier |
| `example_id` | `String` | Evaluated example identifier |
| `offline_trace_id` | `Nullable(String)` | Agent execution trace identifier |
| `evaluated_at` | `DateTime64(6)` | Evaluation time in UTC |
| `judge_id` | `String` | Stable judge identifier |
| `judge_major_version` | `UInt32` | Judge major version |
| `judge_minor_version` | `UInt32` | Judge minor version |
| `score_type` | `String` | numeric, binary, or categorical |
| `numeric_value` | `Nullable(Float64)` | Numeric score |
| `boolean_value` | `Nullable(Bool)` | Binary score |
| `categorical_value` | `Nullable(String)` | Categorical score |
| `value_text` | `Nullable(String)` | Normalized score text |
| `reason_json` | `Nullable(String)` | Evaluation reason JSON |
| `metadata_json` | `Nullable(String)` | Evaluation metadata JSON |
| `success` | `Nullable(Bool)` | Optional test pass-condition result |
| `error_message` | `Nullable(String)` | Scorer error |


### datasets.datasets
One current dataset

| Column | Type | Description |
| --- | --- | --- |
| `dataset_id` | `String` | Dataset identifier |
| `name` | `String` | Dataset name |
| `current_version` | `UInt32` | Current version number |
| `created_at` | `DateTime64(6)` | Creation time in UTC |
| `updated_at` | `DateTime64(6)` | Last update time in UTC |
| `schema_json` | `Nullable(String)` | Dataset schema as JSON |


### datasets.examples
One dataset membership per example, including its version interval

| Column | Type | Description |
| --- | --- | --- |
| `dataset_id` | `String` | Dataset identifier |
| `example_id` | `String` | Example identifier |
| `added_at` | `DateTime64(6)` | Time added to the dataset in UTC |
| `example_created_at` | `DateTime64(6)` | Example creation time in UTC |
| `version_added` | `UInt32` | First dataset version containing the example |
| `version_removed` | `Nullable(UInt32)` | First version excluding the example |
| `data_json` | `String` | Example data as JSON |
| `metadata_json` | `String` | User metadata as JSON |
| `managed_metadata_json` | `String` | Judgment-managed metadata as JSON |


### config.judges
One judge with a production version per judge_id

| Column | Type | Description |
| --- | --- | --- |
| `judge_id` | `String` | Stable judge identifier |
| `name` | `String` | Current judge name |
| `description` | `Nullable(String)` | Current description |
| `judge_type` | `String` | Judge implementation type |
| `score_type` | `String` | Judge score type |
| `evaluation_mode` | `String` | Production version evaluation mode |


### config.behaviors
One current label definition per behavior_id

| Column | Type | Description |
| --- | --- | --- |
| `behavior_id` | `String` | Stable label identifier |
| `judge_id` | `String` | Owning judge identifier |
| `value_text` | `String` | Label value |
| `description` | `Nullable(String)` | Label description |
| `created_at` | `DateTime64(6)` | Creation time in UTC |
| `judge_name` | `String` | Current judge name for this label |
| `categories_json` | `String` | Current categories as JSON with id, name, and color, ordered by id; [] when none |


## Examples

### Session timing and usage
```sql
WITH session_roots AS (
  SELECT session_id, min(started_at) AS started_at, max(ended_at) AS ended_at,
         count() AS trace_count
  FROM telemetry.traces
  WHERE session_id IS NOT NULL
  GROUP BY session_id
), session_usage AS (
  SELECT session_id, sum(cost_usd) AS cost_usd,
         sum(non_cached_input_tokens + cache_read_input_tokens + cache_creation_input_tokens) AS input_tokens,
         sum(output_tokens) AS output_tokens
  FROM telemetry.spans
  WHERE session_id IS NOT NULL
  GROUP BY session_id
)
SELECT r.session_id, r.trace_count, r.started_at, r.ended_at,
       dateDiff('microsecond', r.started_at, r.ended_at) / 1000.0 AS elapsed_ms,
       coalesce(u.cost_usd, 0) AS cost_usd,
       coalesce(u.input_tokens, 0) AS input_tokens,
       coalesce(u.output_tokens, 0) AS output_tokens
FROM session_roots AS r
LEFT JOIN session_usage AS u USING (session_id)
ORDER BY r.started_at DESC, r.session_id DESC
LIMIT 50
```

### Current example counts by dataset
```sql
SELECT d.dataset_id, d.name, countIf(e.example_id IS NOT NULL AND e.version_removed IS NULL) AS current_examples
FROM datasets.datasets AS d
LEFT JOIN datasets.examples AS e USING (dataset_id)
GROUP BY d.dataset_id, d.name
ORDER BY current_examples DESC
LIMIT 50
```
