Okay, so now we know we have a query we need to optimize. How do we do it? With an index!

An index makes a B-Tree (which stands for balanced tree) to make look ups much faster. Instead of taking O(n) to do a lookup items, it takes O(log n) since it can use a tree to find the item instead of scanning every item in the database.

CREATE INDEX idx_track_name ON Track (Name);

-- see it's there now
PRAGMA index_list('Track');

EXPLAIN QUERY PLAN SELECT * FROM Track Where name ='Black Dog';
-- `--SEARCH Track USING INDEX idx_track_name (Name=?)

Notice it's a SEARCH now instead of a SCAN. This means it was able to use an index and only look at a subset of the table instead of every row. Hooray!

EXPLAIN QUERY PLAN SELECT * FROM Track Where name ='Black Dog';
-- w/ index
addr  opcode         p1    p2    p3    p4             p5  comment
----  -------------  ----  ----  ----  -------------  --  -------------
0     Init           0     19    0                    0   Start at 19
1     OpenRead       0     409   0     9              0   root=409 iDb=0; Track
2     OpenRead       1     2     0     k(2,,)         2   root=2 iDb=0; idx_track_name
3     String8        0     1     0     Black Dog      0   r[1]='Black Dog'
4     SeekGE         1     18    1     1              0   key=r[1]
5       IdxGT          1     18    1     1              0   key=r[1]
6       DeferredSeek   1     0     0                    0   Move 0 to 1.rowid if needed
7       IdxRowid       1     2     0                    0   r[2]=rowid; Track.rowid
8       Column         1     0     3                    0   r[3]= cursor 1 column 0
9       Column         0     2     4                    0   r[4]= cursor 0 column 2
10      Column         0     3     5                    0   r[5]= cursor 0 column 3
11      Column         0     4     6                    0   r[6]= cursor 0 column 4
12      Column         0     5     7                    0   r[7]= cursor 0 column 5
13      Column         0     6     8                    0   r[8]= cursor 0 column 6
14      Column         0     7     9                    0   r[9]= cursor 0 column 7
15      Column         0     8     10                   0   r[10]= cursor 0 column 8
16      ResultRow      2     9     0                    0   output=r[2..10]
17    Next           1     5     1                    0
18    Halt           0     0     0                    0
19    Transaction    0     0     65    0              1   usesStmtJournal=0
20    Goto           0     1     0                    0

Again, I have a hard time reading this. You can see that it refers to the index in the comments so that's positive.

Okay so let's talk a little bit more about why you may not to index everything. I've heard the saying that indexes are like aspirin – they're a great help when you have a problem but if you use too many they become a problem.

Every time you insert into a table that has indexes, it has to do some rebuilding of the indexes to accommodate this information. Likewise, if you delete, it has to move its nodes around its B-tree to keep it balanced. B-trees also take up space, and on large tables it can be non-trivial amounts of space. The trade-off here is that indexes help with reads but slow down updates, deletes, and inserts as well as take up space. In general I wait for a query to become a problem first before I try to index it, and even then I try to index only what I need to solve my problem. Pre-mature optimization generally is a bad thing to do because as developers we're pretty bad at guessing what's going to go wrong.