Clustering Key Choice on a Continuously Loaded Fact Table
ACME's ORDER_FACT table holds 2.4 billion rows and grows by roughly 40 million rows a day through Snowpipe. Analysts run about 800 queries an hour; 92% of them filter on ORDER_DATE down to the trailing 30 days, and about a third of those also filter on REGION_CODE, which has 18 distinct values. The table is currently clustered on (REGION_CODE, ORDER_DATE). Automatic clustering is suspended to save credits, and the team reloads continuously from a stream of files, so rows do not arrive in date order. The query profile for date-only queries still shows 70-85% of micro-partitions scanned, average latency of 42 seconds on a Large warehouse, and almost no local or remote spilling. The proposed fix is to move to a 3XL warehouse.
1. The team wants to move to a 3XL warehouse. Is that the right first move, and is the current clustering key defensible?
Read the worked answer
No to the resize, no to the key.
The symptom (70-85% of partitions scanned, near-zero spilling) says the cost is in reading micro-partitions, not in computing the result. A larger warehouse adds parallel compute; it does not reduce bytes or partitions read. You would burn 4x the credits per hour for a modest latency gain and the pruning ratio would be unchanged.
The key is the actual defect. Micro-partitions are ordered by the leading column. With REGION_CODE first, all 18 regions are interleaved across the whole table, so a date-only predicate can only prune inside each region band - which is exactly the ~75% scan rate observed. Guidance:
- The leading column must be the most frequent, reasonably selective predicate. Here that is ORDER_DATE (or
TO_DATE(order_ts)if the predicate is a cast). - A low-cardinality leading column such as REGION_CODE gives almost no pruning value.
- Put REGION_CODE second only if measurement shows it helps the one-third of queries that use both.
- Continuous Snowpipe ingestion constantly reorders incoming rows, so any key will drift. Pair the new key with automatic clustering, or the improvement decays within days.
2. How would you choose between automatic clustering, a materialized view, search optimization, and a warehouse resize? What evidence would you require before spending credits?
Read the worked answer
Work backwards from the profile, not from intuition:
| Evidence in the profile | Candidate fix |
|---|---|
| High partitions scanned / total, no spill | Clustering (or a pre-aggregate) |
| Spill to local or remote disk, time dominated by join/aggregate | Scale up the warehouse, or fix the join |
| Selective point lookups on a high-cardinality column (e.g. ORDER_ID) | Search optimization service |
| The same 30-day aggregate recomputed hundreds of times a day | Materialized view or a task-refreshed summary table |
For clustering specifically, run SYSTEM$CLUSTERING_INFORMATION before and after to see average depth and overlap, and budget the maintenance cost from SNOWFLAKE.ACCOUNT_USAGE.AUTOMATIC_CLUSTERING_HISTORY - automatic clustering is billed as serverless credits and scales with table size and DML volume. On a table receiving 40M rows a day that bill can exceed the savings, which is why the team suspended it.
Note that the result cache will not save this workload: the continuous load invalidates cached results for ORDER_FACT almost immediately.
Recommendation: test a (ORDER_DATE) key with automatic clustering for two weeks, and in parallel pilot a materialized view for the trailing-30-day aggregate that drives most dashboard traffic. Compare credits per query in QUERY_HISTORY, not just latency.
3. What could make clustering the wrong answer here, and how would you prove the change worked?
Read the worked answer
Clustering buys nothing when pruning is impossible in principle:
- Queries that scan the full date range (all-time totals, cohort analyses) cannot prune on ORDER_DATE.
- Queries dominated by compute - large joins, window functions, sorts - do not care how the rows are ordered on disk.
- Tables that are small relative to warehouse memory gain little from pruning.
If the dominant pattern really is "always the trailing 30 days," then the cheapest architecture is usually a narrow, pre-aggregated table (or a materialized view) holding only that window, maintained by a scheduled task. That reduces bytes scanned by an order of magnitude without paying to re-cluster 2.4 billion rows.
To prove it worked, do not rely on anecdote:
- Capture a fixed 50-query workload from
QUERY_HISTORYand store the SQL. - Record baseline: partitions scanned, bytes scanned, elapsed time, credits per query.
- Replay after the change under the same warehouse size and concurrency.
- Watch
AUTOMATIC_CLUSTERING_HISTORYover at least one full ingestion week, because re-clustering cost lags the key change.
Also account for the fact that changing a clustering key is not free - the initial re-cluster of a large table consumes credits in one burst, so schedule it deliberately.