Snowflake Optimizing Clustering Keys

Problem Statement

Decide which frequently run queries are worth optimizing with clustering keys and materialized views, balancing performance gains against compute and storage costs.

Full example scenario

As a data engineer at a software company, you are tasked with optimizing the performance of a critical application running on Snowflake. The database handles a large volume of read and write operations, and the current table structures and clustering keys are not optimized, leading to slow query performance and higher costs associated with compute time and storage. Your goal is to design the most efficient clustering strategy and optimize materialized views to enhance query performance while minimizing the impact on storage and compute costs. You have a set of queries that are frequently run against the database, each with a different frequency and selectivity. Which queries should be optimized, in order to maximize the total query performance improvement — calculated as the sum of the chosen queries’ performance improvements divided by the total number of queries?

  • The total additional compute cost introduced by the clustering keys and materialized views must not exceed 20 units.

  • The total storage used by all optimizations must not exceed 45 GB.

  • The total query performance improvement must be at least 20%.

  • The query data can be found in snowflake.csv, with the columns Query_ID,Frequency,Selectivity (%),Compute_Maintenance_Cost,Storage_Cost (GB),Performance_Improvement (%).

What an optimal solution looks like

Both axes are running totals: cumulative storage spent (x) against cumulative performance gained (y), adding one query at a time in order of performance-per-GB. The dashed line adds greedily and stops early; the solid line is the true optimum.

True optimum — fills all 45 GB (118% total) Greedy by ratio — stalls at 39 GB (105% total) 45 GB storage budget
10 20 30 40 0 20 40 60 80 100 120 0 Cumulative storage used (GB) Cumulative performance gained (%) 45 GB budget Q8 Q3 Q4 Q9 Q2 Q1 Q9 Q2 Q1 Q5 Optimum: 45/45 GB → 118% total Greedy: 39/45 GB → 105% total

Order of addition — greedy: Q8 → Q3 → Q4 → Q9 → Q2 → Q1, then stuck with 6 GB spare and nothing left small enough to use it. Optimum: Q8 → Q3 → Q9 → Q2 → Q1 → Q5, landing exactly on the 45 GB wall.

Both paths agree on the first two picks (Q8, then Q3 — the two most efficient queries per GB). They part ways on the third pick: greedy takes Q4 next because its ratio (17/6 ≈ 2.83) beats what's left, but that choice leaves only 6 GB of headroom — too little for anything else, including Q5 (12 GB, the single biggest remaining gain at +30%). The optimum skips Q4, keeps the room, and ends up fitting Q5 in after all. Sorting by ratio points roughly the right way, but only a full combinatorial search finds the exact mix that spends the whole budget.

Built with Modeler

The problem above is stated as a single shot: you hand over the whole problem and get a model back. Modeler takes the other route — you start with the business question you actually have, and it works with you until the problem is pinned down, the data is in, the code runs, and the answer holds up when the problem changes.

The tabs below walk the same query-optimization problem through that route, one Modeler capability at a time.

Instead of writing the whole prompt up front, you start with the business question you actually have. Modeler searches the knowledge base, asks one clarifying question at a time — always the most important one first — and tells you which parts of the model are still missing. The options it offers come from examples in the knowledge base and from how similar problems are usually modeled in your industry, so you can answer with a number instead of prose.

I’m a data engineer at a software company. Our Snowflake database is getting hit with a lot of read/write traffic and query performance is suffering — compute and storage costs are creeping up too. We run a set of queries repeatedly, and I think if we add clustering keys / materialized views for the right subset of them we could meaningfully speed things up, but every one we optimize adds ongoing compute and storage overhead. Can you help me build a model to decide which queries are worth optimizing?

This is a binary selection problem — a “query optimization knapsack” that trades off performance benefit against resource overhead. Before anything else I need to know whether clustering keys and materialized views are two separately selectable techniques per query, or one combined choice, since that decides what the decision variable even represents.

Great question. This is a classic binary selection problem — think of it as a “query optimization knapsack” where you want to maximize the performance (or cost) benefit of optimizing a subset of queries while staying within compute and storage budgets. Let me ask clarifying questions one at a time to build a complete specification.

To start: when you say “optimize” a query, you mentioned clustering keys and materialized views as two distinct techniques. Can a single query benefit from both at the same time (i.e., both a clustering key and a materialized view applied independently and additively), or is it an either-or choice per query?

Learn More:
• Poolsearch Examples ★★☆
• Resource Assignment Problem formulation ★☆☆

It’s an either-or choice per query — each query gets one combined “optimization package” (clustering key plus materialized view together) or nothing, not two separate techniques applied independently. So think of each query as a single yes/no decision.

Here are the constraints I know about: the total extra compute-maintenance cost across everything we optimize can’t exceed 20 units, total extra storage can’t exceed 45 GB, and we need the overall query performance improvement to be at least 20%. Performance improvement is measured as the sum of the chosen queries’ Performance_Improvement values, divided by the total number of queries we have (not just the ones we optimize) — and we want to maximize that ratio.

One more thing — we also want to guarantee at least 3 queries get optimized. A plan that technically maximizes performance by picking just one or two “best” queries feels too thin a rollout for a database this size.

That settles the decision structure (one binary per query) and gives me three numeric constraints plus a volunteered fourth — a rollout floor of at least 3 queries. Recording all of it. What is still open is how the team wants the result reported back, which shapes what the output section of the specification should promise.

Good. Now let me ask about the output. When the model produces a solution, what does your team need to see? For example:

  1. Just the list of query IDs selected for optimization

  2. The list plus the achieved performance improvement ratio and total compute/storage usage

  3. All of the above plus a per-query breakdown (e.g., the individual performance improvement and cost of each selected query)

  4. Something else entirely (e.g., a report format, a ranked list)

Which of these best matches what you need, or would you combine a few of them?

Rather than defaulting to something reasonable, Modeler asks one more time — this last open item (the output format) gets settled together with the data upload in the next tab. Modeler updates the specification after every answer, even while it is still incomplete, so you can always see what it has understood and what it is still waiting on.