The Hidden Graph Inside Every Fast SQL Query
Hatched by Kai Nguyen
Sep 08, 2026
11 min read
1 views
92%
What if SQL optimization is not primarily about writing cleverer queries, but about discovering the right order in which information should become available?
A database query may look like a sentence: select these columns, from this table, where these conditions apply. But beneath that readable surface is a dependency problem. Some operations cannot happen until others have produced the information they require. Some operations reduce the amount of data dramatically. Others merely rearrange or decorate it. The difference between a slow query and a fast one often comes down to whether the database can expose and exploit that structure.
This creates a powerful connection between two ideas that are usually taught separately: topological sorting in graphs and SQL query optimization. Topological sorting asks how to arrange dependent tasks into a valid sequence. Query optimization asks how to arrange relational operations into an efficient execution plan.
The deeper lesson is this:
A valid order gets the right answer. An intelligent order gets the answer without carrying unnecessary work through every later step.
That distinction explains why indexes matter, why functions on indexed columns can be disastrous, why filtering early is usually valuable, and why the order written in a query is not necessarily the order in which the database executes it.
Every Query Is a Dependency Graph in Disguise
Consider a simple request:
SELECT name
FROM customers
WHERE country = 'Canada'
AND signup_date >= '2025-01-01';
At the surface, this looks linear. Read the table, apply the conditions, return the name. Internally, however, the database must coordinate several operations: identify the relevant relation, locate candidate rows, evaluate predicates, project the requested column, and produce the result.
These operations are not all interchangeable. The database cannot return the final name values before it knows which rows qualify. It may not need to inspect every row if an index can identify matching records directly. It may also choose between several legal plans depending on table size, index availability, statistics, and estimated selectivity.
This resembles a directed graph. Each operation is a node. A dependency is an edge. If operation B requires information produced by operation A, the graph contains an edge from A to B. A topological ordering is any sequence that respects all these dependencies.
In graph terminology, a node with no incoming edges is a source. It can be processed because nothing else must happen first. A node with no outgoing edges is a sink. It feeds no later operation. In a query plan, base table access often behaves like an early source, while final projection or result delivery behaves like a sink.
But legal ordering is only the beginning. A graph can have many valid topological orderings, and they may have radically different costs. If a query plan carries ten million rows through a sort and then discards nine million of them, it is technically correct but operationally wasteful. If it filters down to ten thousand rows before sorting, the result is the same, but the work is transformed.
The central optimization question is therefore not merely, “What order is allowed?” It is:
Which legal order causes the largest amount of irrelevant work to disappear as early as possible?
The Difference Between Dependency and Selectivity
Topological sorting supplies a constraint: do not violate dependencies. Query optimization adds a second dimension: prioritize operations that reduce future work.
This can be understood through two properties of an operation.
First is dependency depth. How much information must be available before the operation can run? A computation requiring grouped results must wait until rows have been gathered into groups. A filter based only on a base table column may be available almost immediately.
Second is selectivity. How sharply does the operation reduce the number of candidates? A condition that eliminates 99 percent of rows is highly selective. A condition that eliminates 2 percent is weakly selective.
A useful mental model is to imagine every intermediate result as a crowd moving through a series of doors. Each operation is a door. Some doors are narrow and remove most people. Others are wide and let nearly everyone pass. If a narrow door is legally available near the entrance, using it early prevents the crowd from occupying every room beyond it.
Suppose a table contains ten million events. The query needs events from one customer during one week, then sorts them by timestamp. Two possible plans are:
- Read all ten million rows, sort them, then filter by customer and date.
- Locate the relevant customer and date range first, then sort the much smaller result.
Both plans can produce the same rows. Their resource requirements are not remotely equivalent. Sorting a small set is cheap. Sorting a massive set merely to throw most of it away is an example of preserving correctness while ignoring graph economics.
This is why “filter early” is more than a stylistic rule. It is a strategy for shrinking the graph’s intermediate states. The database is not only choosing an order of operations. It is choosing how many rows, pages, comparisons, and calculations each later operation must endure.
The fastest operation is often the one you prevent from happening to data that never needed to reach it.
SARGability: Keeping the Useful Edge Visible
An index is valuable because it gives the optimizer a navigable structure. Instead of examining every row, the database can move directly toward likely matches. But this advantage depends on the predicate being expressed in a form the index can use.
Consider these two conditions:
WHERE price * 1.2 > 100
and:
WHERE price > 100 / 1.2
They are mathematically equivalent. They are not necessarily equivalent to the optimizer. In the first version, the indexed column is wrapped in an arithmetic operation. The database may be unable to use the index as a direct search path, forcing it to calculate price * 1.2 for many rows. In the second version, the column remains exposed as the searchable object.
This is the idea behind SARGability, short for Search Argument Able. A SARGable predicate allows the system to turn a condition into an efficient search operation, often through an index. The crucial concept is not simply that an index exists. It is that the query preserves the relationship between the indexed value and the desired range or equality.
The distinction can be viewed graphically. An index creates a useful edge from a condition to a restricted set of records. Applying a function to the indexed column can hide that edge behind a transformation. The information is still present mathematically, but it is no longer presented in a form the execution engine can easily traverse.
For example:
WHERE YEAR(created_at) = 2025
may require evaluating YEAR across many rows. A range predicate exposes the underlying order of the timestamp:
WHERE created_at >= '2025-01-01'
AND created_at < '2026-01-01'
The second form gives the database a clear interval to seek. It turns a general computation into a navigable boundary.
The same principle explains why leading wildcards are problematic:
WHERE email LIKE '%@example.com'
An index ordered from the beginning of the string cannot easily jump to a known starting point when the pattern begins with an unknown sequence. By contrast:
WHERE email LIKE 'admin%'
provides a usable prefix. The index can locate the region where matching values begin and stop when the prefix no longer matches.
Negation can create a similar problem. A condition such as:
WHERE status <> 'archived'
may be logically clear but less useful as an index search than a positive, selective condition. If most rows are not archived, the predicate does little to narrow the search anyway. The issue is not that negation is always forbidden. The issue is that it often produces a weak or opaque path through the data.
Logical Order, Physical Order, and the Optimizer’s Freedom
SQL has a written order that is convenient for humans. We write SELECT, then FROM, then WHERE, followed by grouping, filtering of groups, ordering, and limiting. The database, however, is not obligated to execute the query in that visual order.
This apparent contradiction is essential. SQL describes what result is wanted. The optimizer searches for a legal physical plan that produces it. The declared query establishes semantic dependencies, but it leaves room for the engine to reorder operations when the result remains unchanged.
For instance, a WHERE condition usually reduces rows before grouping. A HAVING condition applies after groups exist because it depends on aggregate results. That dependency restricts the graph. But within those constraints, the engine may push a filter closer to the table scan, choose an index, reorder joins, or avoid materializing unnecessary data.
This is the database version of topological sorting. The optimizer looks for a legal arrangement among many possibilities. It also estimates the cost of each arrangement. A plan that filters early may be better, but only if the filter is selective and usable. An index may exist, but if it matches a huge portion of the table, a sequential scan can be cheaper. An ordering may be valid, but if it requires a costly sort, the optimizer may exploit an existing index instead.
The important distinction is between semantic order and execution order.
Semantic order answers: “What must be true for this result to mean what it means?”
Execution order answers: “What sequence minimizes the resources needed to establish that meaning?”
Confusing these two leads to poor optimization habits. Developers sometimes assume that placing a condition earlier in the written query guarantees earlier evaluation. It does not. Conversely, writing a clear predicate in a SARGable form gives the optimizer more options, because it reveals a useful dependency and a possible access path.
The best SQL often does not micromanage every step. It expresses constraints clearly, exposes searchable columns, limits unnecessary output, and removes needless calculations. It makes good plans easier to discover.
A Practical Framework: Sources, Bottlenecks, and Sinks
The graph analogy becomes especially useful when debugging a slow query. Instead of staring at SQL syntax, classify each operation by its position and effect.
1. Find the sources
Sources are operations that can begin with minimal prerequisites. These usually include base table access and simple predicates on stored columns. Ask whether the database can reach these sources efficiently through an appropriate index.
If a query begins with a full scan of a very large table, determine whether the filtering conditions could support an index. Consider equality conditions, useful ranges, and columns commonly used together. The goal is not to index everything. Every index adds storage and maintenance cost. The goal is to create a strong early path for common selective searches.
2. Identify the narrow doors
Look for predicates that can eliminate large portions of the candidate set. Move conceptually toward these operations as early as the dependency graph permits. Keep their indexed columns visible. Avoid wrapping them in functions, arithmetic, or expressions when the same logic can be expressed as a range or direct comparison.
For example, replace a date extraction function with a half open interval. Replace an expression on a numeric column with an equivalent boundary on the column itself. Replace an unnecessary leading wildcard with a searchable prefix when the product requirement allows it.
3. Locate the expensive middle
Sorting, grouping, joining, aggregation, and repeated calculations often become expensive in proportion to the size of their input. They are not automatically bad. They become dangerous when fed by an unnecessarily large intermediate result.
Ask: “Can this operation receive fewer rows?” If yes, seek an earlier filter, a narrower projection, a more suitable index, or a query rewrite that avoids calculating values the final result does not need.
4. Protect the sinks
The final result is a sink, but it still matters. Returning millions of rows imposes costs on the database, network, application, and user. Use limits when the interface needs only a page. Select only required columns rather than requesting every field. Avoid unnecessary sorting when the consumer does not require a particular order.
A sink that receives too much data can make every earlier improvement feel insignificant. The final transfer is part of the plan, not an afterthought.
5. Verify the graph with evidence
Intuition is useful for forming hypotheses, but execution plans and measurement should decide. Inspect whether an index is actually used, how many rows each operation receives, where estimates diverge from reality, and which step consumes the most time or memory.
The execution plan is effectively a map of the optimizer’s chosen graph. It shows where rows enter, where they are reduced, where they multiply through joins, and where expensive work occurs. Reading it this way is more productive than treating it as an intimidating list of database internals.
Key Takeaways
-
Think of a query as a dependency graph. Separate the order required for correctness from the order chosen for efficiency.
-
Reduce intermediate results early. A selective filter is most valuable before sorting, grouping, joining, or calculating over large sets.
-
Preserve SARGability. Keep indexed columns exposed in direct comparisons or ranges. Rewrite functions, arithmetic, and patterns that hide searchable boundaries.
-
Use indexes as paths, not decorations. An index helps only when the predicate and data distribution allow the optimizer to traverse it profitably.
-
Read execution plans as flow diagrams. Find the sources, the largest intermediate result, the expensive bottleneck, and the final data transfer.
The most important shift is to stop thinking of optimization as a collection of isolated tricks. “Use indexes,” “filter early,” “avoid unnecessary sorting,” and “limit results” are not unrelated commandments. They are consequences of one underlying principle: control the size and visibility of intermediate states.
Topological sorting teaches that dependencies constrain order but often leave multiple valid sequences. SQL optimization adds the insight that those sequences have economic consequences. A plan can be correct while needlessly carrying a vast population of irrelevant rows through every stage. A better plan makes the graph narrower as soon as the dependencies allow it.
That is why a fast query is not merely a short query, and an index is not merely an object sitting in a schema. Performance emerges when the structure of the request makes useful paths visible and allows waste to be eliminated before it compounds.
The next time a query runs slowly, do not ask only, “Which clause is wrong?” Ask a more revealing question: Where in this graph could irrelevant work have disappeared, and what is preventing that disappearance?
Sources
Hatch New Ideas with Glasp AI 🐣
Glasp AI allows you to hatch new ideas based on your curated content. Let's curate and create with Glasp AI :)
Start Hatching 🐣