Crosshire
← Databricks Audit
— Crosshire / Learn · Tech · Databricks · Audit

Query Performance

Which SQL statements are slow, wasteful or failing — and why. Heaviest statements, file pruning, disk spill, shuffle, cache cold-start, queuing, failures and workload mix.

12 queriesbuilt on system.*

System tables used

system.query.history

What it is. A record of completed query/statement executions across the account's compute, with rich per-statement performance and I/O counters. This is the system-table equivalent of the SQL editor's Query History, exposed for SQL analytics rather than one-off UI inspection.

Grain. One row per completed statement execution (identified by statement_id). Aggregating queries in this folder roll that grain up to day × workspace × warehouse × statement_type × user, etc.; the detail queries (query_costly_statements, query_per_query_estimate_lane) keep the native one-row-per-statement grain.

Key columns these queries use:

  • statement_id — unique id of the statement execution (join/dedup key; row grain).
  • statement_type — SELECT / INSERT / MERGE / UPDATE / etc.; used to split workload and to find write-heavy statements.
  • statement_text — the SQL text. Used to detect the audit's own marker (ILIKE '%databricks_audit%') and, in detail queries, emitted only after emails and single-quoted string literals are stripped at source (shape kept, data values removed).
  • execution_status — enum FINISHED / FAILED / CANCELED. Filters "successful only" lanes and drives the failed-queries report.
  • error_message — failure text (de-valued at source). Note: empty under customer-managed keys (CMK).
  • executed_by — the principal (user email or service-principal GUID). Always partial-masked in output (e.g. da**@**; GUIDs kept as opaque handles). identity_type is derived from whether it looks like an email.
  • start_time — statement start timestamp; the sole date-window filter and the basis for day / hour_of_day / usage_hour bucketing.
  • total_duration_ms — end-to-end wall time (includes waiting).
  • execution_duration_ms — pure execution time; used as the cost proxy (warehouse DBUs are allocated ~ proportionally to it) for ranking and for the per-query estimate lane.
  • total_task_duration_ms — summed task (CPU) time across the cluster; a parallelism/compute-intensity signal.
  • waiting_for_compute_duration_ms — time waiting for compute to provision (cold-start / warehouse spin-up).
  • waiting_at_capacity_duration_ms — time queued because the warehouse was at capacity.
  • compilation_duration_ms — metadata/optimizer/compile time (planner-bound latency).
  • read_bytes, read_files, read_partitions, read_rows — scan I/O; read_partitions is a post-pruning count, not "partitions pruned".
  • pruned_files — files skipped by data/file pruning; pruning effectiveness = pruned_files / (pruned_files + read_files).
  • produced_rows — rows returned to the client (distinct from read_rows).
  • spilled_local_bytes — bytes spilled to local disk (memory pressure). Local spill only — there is no spilled_remote_bytes column in Databricks.
  • shuffle_read_bytes — shuffle volume; a bad-join / shuffle-heavy signal.
  • written_bytes, written_rows, written_files — write output; written_files vs written_rows flags the small-files / write-amplification problem.
  • from_result_cache (boolean) — served from the result cache. Distinct from…
  • read_io_cache_percent — % of scan bytes served from the disk/IO cache (NOT the same signal as from_result_cache).
  • compute (struct) — compute.type (e.g. warehouse vs. serverless) and compute.warehouse_id. Serverless rows carry warehouse_id = NULL.
  • query_source (nested struct) — provenance: query_source.job_info.job_id, .dashboard_id, .legacy_dashboard_id, .notebook_id, .alert_id, .genie_space_id, .sql_query_id. Each subfield is NULL when that entity wasn't involved, and multiple subfields can populate simultaneously (they are not execution-ordered), so single-winner CASE attribution is a heuristic.
  • workspace_id — the workspace that ran the statement (every query groups by this).

Availability.

  • Unity Catalog required; you must have SELECT on system.query.history (system schemas are access-controlled, granted per-metastore by an admin).
  • The query system schema may need to be enabled per-metastore and has been in Preview — an unqueryable/undisabled schema yields TABLE_OR_VIEW_NOT_FOUND.
  • Regional / per-region: history is scoped to the region; attribute costs only within-region.
  • Coverage is compute-limited: captures SQL warehouses and serverless; classic / all-purpose (interactive) cluster statements are NOT recorded here. So spill, shuffle, and pruning signals for classic-cluster jobs will simply be absent.
  • Empty-if-unused: a workspace with no matching activity (e.g. no failed queries, no serverless usage) returns zero rows — valid, not an error.
  • CMK degradation: under customer-managed encryption keys, error_message comes back blank.
  • Short ingest latency — the most recent minutes of an in-flight run may not appear yet.

system.billing.usage (referenced downstream only)

Not read by any .sql file in this folder. Because system.query.history has no per-statement dollar/DBU column, the per-query estimate lane (query_per_query_estimate_lane) emits only the raw drivers (execution_duration_ms, total_task_duration_ms, read_bytes), and the actual dollar attribution happens downstream by weighting hourly warehouse DBUs from system.billing.usage and reconciling so per-query estimates sum to metered DBU. See the billing/cost domain README for that table's grain, columns, and availability.

Before you run these

  • Databricks system.* schemas aren’t uniformly available — several (e.g. system.query, system.lakeflow) are still in preview or must be enabled per-metastore. A query that returns nothing usually means its schema isn’t enabled for your account yet.
  • Every dollar figure is est · at list (pre-discount, DBU-only from system.billing.list_prices) unless a query explicitly joins account_prices.
  • Sample-output tables are illustrative synthetic rows, never real account data.

Queries in this domain

01Your Heaviest Statements, Ranked query_costly_statementsThe top 1000 finished, non-cached SQL statements in the trailing window, ranked by execution time (a DBU-cost proxy), each carrying pruning, spill, shuffle, and scan counters plus a de-valued query text to diagnose the fix.02What This Audit Costs Youaudit_self_costA per-workspace, per-statement-type tally of the audit's own queries — how many ran, how long they took in wall-clock and task time, how many principals ran them, and the first/last time they landed — so you can see the cost footprint of running CrossHire itself.03Cache Hits vs. Cold-Start Latencyquery_cache_coldstartA daily per-warehouse rollup that separates the two distinct cache signals (result-cache hits and disk/IO cache coverage) from cold-start and compile-bound latency.04Daily Failed & Canceled Queriesquery_failed_queries_dailyA daily rollup of FAILED and CANCELED statement executions — counts, wasted runtime, and a de-valued error-message sample — sliced by workspace, warehouse, compute type, statement type, and (masked) user.05Disk Spill Memory-Pressure Hotspotsquery_local_spillageA daily rollup of statements that spilled to local disk, showing per day, workspace, compute type, warehouse, and (masked) user how many queries spilled, how much they spilled, and their duration and shuffle context.06Per-Query Cost Allocation Inputsquery_per_query_estimate_laneOne row per finished, non-cached warehouse statement, carrying the raw duration, task-time and scan-bytes drivers (bucketed by hour) that a downstream job weights to estimate each query's share of metered warehouse DBUs.07Where Query Spend Comes Fromquery_provenance_by_sourceA grouped rollup of the last 30 complete days of statement executions, attributing each to its originating source (job, dashboard, legacy_dashboard, notebook, alert, genie, sql_editor) and identity type (human user vs. service principal), with query counts, execution/wall-clock time, and scan bytes per workspace and warehouse.08Are Your Scans Skipping Files?query_pruning_effectivenessA daily rollup of files pruned versus files read (plus partitions, bytes, and rows scanned) for scan statements, sliced by workspace, warehouse, user, and statement type, so you can spot workloads doing full scans instead of pruning.09Capacity Queuing vs Cold-Start Waitsquery_queuing_waitsA daily, per-warehouse rollup that separates time queries spent queued because a warehouse was at capacity from time they spent waiting for compute to spin up.10Shuffle And Small-Files Hotspotsquery_shuffle_write_amplificationA daily rollup of shuffle-read volume and write output (bytes, rows, files) for shuffle-heavy or write-heavy statements, sliced by workspace, compute type, warehouse, masked user, and statement type.11When And What Runsquery_workload_mix_hoursA day-by-hour histogram of query volume, runtime, scan bytes, and rows returned, sliced by workspace, compute type, warehouse, statement type, and (masked) user.12Heaviest Query Shapes, Grouped by Fingerprintquery_costly_statements_groupedThe heaviest SQL rolled up by statement shape (fingerprint), so a cheap statement run ten thousand times ranks next to one genuinely heavy query — and you can tell which one to fix.