In this chapter
We give AllOrders its first real index and watch EXPLAIN QUERY PLAN flip from SCAN to SEARCH — then look at why a sorted B-Tree makes that possible in just a few comparisons instead of thousands.
The Problem in Real Life
Sarah pulls a real book off the shelf behind the counter and flips to the back page. "When you look up a word in here, you don't read every page front to back. You use the index — it tells you exactly which page to go to."
Mike nods slowly. "So the database... doesn't have one of those?"
"Not unless we build it one," Sarah says. "Right now, every single lookup means checking every single row. We can give it an actual shortcut."
It's not that the database is slow — it just doesn't have an index yet.
Sarah
Reading Every Page vs. Using the Index
A table with no index has no shortcuts
Without an index, every lookup means checking every row — there's no faster path available.
An index is a second, sorted structure
It's built and maintained separately from the table, specifically to make lookups on certain columns fast.
B-Trees stay balanced as they grow
Doubling the number of rows adds only one more comparison to a B-Tree lookup, not double the work.
Every index costs something on writes
Each INSERT, UPDATE, or DELETE now also has to update every index on that table, not just the row itself.
What Makes a B-Tree Fast?
An index is a separate structure the database keeps, sorted by the column(s) you tell it to track — built specifically so it can jump straight to matching rows instead of checking every one. It costs extra space and a little extra work on every write, in exchange for dramatically less work on reads.
Almost every database — SQLite included — builds its indexes as a B-Tree: a balanced, sorted tree structure where every lookup starts at one root node and narrows down in just a handful of steps, no matter how large the table gets.
Finding CustomerID 250 in a B-Tree
Root
Which third of the table?
IDs 1–166
IDs 167–333
250 is here
IDs 334–500
Leaf: CustomerID 250
Points straight at the matching rows
EXPLAIN QUERY PLANSELECT * FROM AllOrders WHERE CustomerID = 250;
SCAN AllOrders — exactly what Chapter 1 showed. With no index, this is the only strategy SQLite has.
CREATE INDEX tells the database to build and maintain a sorted B-Tree on the named column(s) — from this point on, every INSERT, UPDATE, or DELETE also updates the index, not just the table.
CREATE INDEX idx_allorders_customerid ON AllOrders (CustomerID);
This runs once and returns no rows — the index now exists, silently, for every future query to use.
EXPLAIN QUERY PLANSELECT * FROM AllOrders WHERE CustomerID = 250;
SEARCH AllOrders USING INDEX idx_allorders_customerid (CustomerID=?) — SQLite now jumps directly to matching rows instead of checking all 10,000.
A B-Tree is sorted, so it's just as good at finding a range of values as it is at finding one exact match — it jumps to where the range starts, then reads forward only as far as it needs to.
EXPLAIN QUERY PLANSELECT * FROM AllOrders WHERE CustomerID BETWEEN 100 AND 105;
SEARCH AllOrders USING INDEX idx_allorders_customerid (CustomerID>? AND CustomerID<?) — still a targeted search, not a scan.
That's the entire trick: instead of one giant pile of rows in no particular order, a B-Tree keeps things sorted and branching, so each step through it eliminates a huge chunk of the remaining possibilities — a handful of comparisons replaces thousands of individual row checks.
But an index isn't free. It takes real disk space, and every write to the table now has to update the index's sorted structure too. That trade-off — faster reads in exchange for slightly slower writes and more storage — is exactly what the next chapter digs into, because it's why you can't just index every column and call it done.
Key Takeaway
A B-Tree index turns a full table scan into a targeted search by keeping data sorted and branching — a handful of comparisons replaces checking every single row, and the saving gets bigger, not smaller, as the table grows.
Why This Matters
Indexes are the single highest-leverage performance tool in any relational database — a missing index is the most common root cause of a query that 'used to be fast,' and adding the right one is often a one-line fix for a problem that looked like it needed a rewrite.
GreenMart's slow lookup now runs as a targeted SEARCH instead of a full SCAN. But not every column deserves an index, and not every index is the same shape — that's next.
