Bytes scanned measures one step, not the whole query

In Snowflake's QUERY_HISTORY, TOTAL_ELAPSED_TIME is the sum of several phases that have nothing to do with how much data was ultimately read: COMPILATION_TIME (parsing and optimizing the plan), QUEUED_PROVISIONING_TIME (waiting for compute to spin up), QUEUED_OVERLOAD_TIME (waiting in line behind other queries on a busy warehouse) -- and only then EXECUTION_TIME, which is the phase BYTES_SCANNED actually describes. A query can report a tiny BYTES_SCANNED and still have a large TOTAL_ELAPSED_TIME, because most of that time came from a phase the scan size never touches. Less commonly, the scan itself is inflated in a way the final result size hides completely -- covered in Cause 4 below.

See it side by side

Illustrative example built from typical query profile numbers -- not a live connection to Snowflake

Same 12-row result, four different bottlenecks

Click a scenario. Watch which segment grows -- and which numbers barely move at all.

Advertisement

Cause 1: queued behind other work

A single-cluster warehouse can only execute a limited number of queries at once. Once that concurrency limit is hit, new queries wait in line before they start running at all -- Snowflake records this as QUEUED_OVERLOAD_TIME, and it adds directly to TOTAL_ELAPSED_TIME with zero bytes scanned, because the query hasn't started yet. This is the single most common reason a "simple" query feels slow during peak hours and fast at 2am on the exact same warehouse.

Fix: a multi-cluster warehouse that can add clusters under load absorbs the extra concurrency instead of making queries wait; separating a noisy workload (a scheduled ETL job, a BI tool hammering the warehouse with dashboard refreshes) onto its own warehouse removes the contention entirely.

Cause 2: a suspended warehouse had to provision

QUEUED_PROVISIONING_TIME is time "spent in the warehouse queue, waiting for warehouse compute resources to provision, due to warehouse creation, resume, or resize." The first query after an auto-suspend pays this cost before compiling even starts -- and execution on that same first query often runs a little slower on top of it, because the warehouse's local disk cache was dropped when it suspended, so the first pass has to re-read from remote storage cold instead of from cache.

Fix: this is a real trade-off, not a bug -- aggressive auto-suspend saves credits during idle periods but adds this latency to the first query of every new session. For latency-sensitive dashboards, a longer auto-suspend window (or a small always-on warehouse) trades a bit of idle-time cost for consistently warm starts.

Cause 3: compilation time, before a byte is touched

COMPILATION_TIME covers parsing the query and working out an execution plan -- entirely before a single micro-partition is opened. A query built from many joins, a long chain of nested views, or several layers of CTEs can spend real, measurable time here regardless of how little data the finished plan will eventually touch. This is the one cause on this list that has nothing to do with the warehouse at all -- it's pure planning overhead on the query text itself, and it shows up identically whether the warehouse is warm, cold, idle, or busy.

Advertisement

Cause 4: poor micro-partition pruning

Snowflake automatically divides table data into micro-partitions of roughly 50-500 MB of uncompressed data each, and stores metadata for every partition -- including the min/max range of values in each column. When a query filters on that column, the optimizer prunes (skips entirely) any partition whose range can't contain a match. Well-clustered on a date column, a query filtering to one hour out of a year's data can ideally skip all but roughly 1 in 8,760 of the table's partitions.

Poor clustering breaks this: if partitions' value ranges overlap heavily, the optimizer can't safely rule most of them out from the metadata alone, and has to open them to check. The result set can still come back tiny -- the WHERE clause is genuinely selective -- while PARTITIONS_SCANNED is a large fraction of PARTITIONS_TOTAL and EXECUTION_TIME is correspondingly high. SYSTEM$CLUSTERING_DEPTH and SYSTEM$CLUSTERING_INFORMATION measure exactly this -- a lower average depth means better-clustered, more prunable partitions.

Key takeaways

  • TOTAL_ELAPSED_TIME = COMPILATION_TIME + QUEUED_PROVISIONING_TIME + QUEUED_OVERLOAD_TIME + EXECUTION_TIME -- a small BYTES_SCANNED only rules out the last one.
  • Queueing and cold starts are warehouse-level problems, not query-level ones -- they show up identically on a trivial SELECT and a complex analytical query run on the same warehouse at the same moment.
  • Compilation time scales with query complexity (joins, nested views, CTEs), not with how much data the query will touch.
  • A tiny result set doesn't prove a tiny scan -- check PARTITIONS_SCANNED against PARTITIONS_TOTAL before ruling out poor clustering.

Diagnostic checklist

Next time a query feels slower than its result size suggests, pull it up in QUERY_HISTORY and check, in order: is QUEUED_OVERLOAD_TIME or QUEUED_PROVISIONING_TIME non-trivial? Is COMPILATION_TIME unusually high for how simple the query looks? Is PARTITIONS_SCANNED close to PARTITIONS_TOTAL despite a selective filter? The downloadable script below runs all three checks, plus the two SYSTEM$CLUSTERING_* functions for a specific table.

Download pruning-diagnostics.sql
Real, ready-to-run queries for your own Snowflake account -- QUERY_HISTORY timing breakdown + clustering depth check.