Skip to main content
This tutorial shows how to apply to optimize a query against the .

Before you begin

Start the on a 3-node CockroachDB demo cluster with a larger data set.
It’s common to offer users promo codes to increase usage and customer loyalty. In this scenario, you want to find the 10 users who have taken the highest number of rides on a given date, and offer them promo codes that provide a 10% discount. To phrase it in the form of a question: “Who are the top 10 users by number of rides on a given date?”

Rule 1. Scan as few rows as possible

First, study the schema so you understand the relationships between the tables. Run :
Look at the schema for the users table:
There’s no information about the number of rides taken here, nor anything about the days on which rides occurred. Luckily, there is also a rides table. Let’s look at it:
There is a rider_id field that you can use to match each ride to a user. There is also a start_time field that you can use to filter the rides by date. This means that to get the information you want, you’ll need to do a on the users and rides tables. Next, get the row counts for the tables that you’ll be using in this query. You need to understand which tables are large, and which are small by comparison. You will need this later if you need to verify you are using the right join type. As specified by your command, the users table has 12,500 records, and the rides table has 125,000 records. Because it’s so large, you want to avoid scanning the entire rides table in your query. In this case, you can avoid scanning rides using an index, as shown in the next section.

Rule 2. Use the right index

Here is a query that fetches the right answer to your question: “Who are the top 10 users by number of rides on a given date?”
Unfortunately, this query is a bit slow. 111 milliseconds puts you over the limit where a user feels the system is reacting instantaneously, and you’re still down in the database layer. This data still needs to be sent back to your application and displayed. You can see why if you look at the output of :
The main problem is that you are doing full table scans on both the users and rides tables (see spans: FULL SCAN). This tells you that you do not have indexes on the columns in your WHERE clause, which is . Therefore, you need to create an index on the column in your WHERE clause, in this case: rides.start_time. It’s also possible that there is not an index on the rider_id column that you are doing a join against, which will also hurt performance. Before creating any more indexes, let’s see what indexes already exist on the rides table by running :
As suspected, there are no indexes on start_time or rider_id, so you’ll need to create indexes on those columns. Because another performance best practice is to , create an index on start_time that stores the join key rider_id:
Now that you have an index on the column in your WHERE clause that stores the join key, let’s run the query again:
This query is now running much faster than it was before you added the indexes (111ms vs. 20ms). This means you have an extra 91 milliseconds you can budget towards other areas of your application. To see what changed, look at the output:
As you can see, this query is no longer scanning the entire (larger) rides table. Instead, it is now doing a much smaller range scan against only the values in rides that match the index you just created on the start_time column (12,863 rows instead of 125,000).

Rule 3. Use the right join type

Out of the box, the will select the right join type for your statement in the majority of cases. Therefore, you should only provide in your query if you can prove to yourself through experimentation that the optimizer should be using a different than it is selecting. You can confirm that in this case the optimizer has already found the right join type for this statement by using a hint to force another join type. For example, you might think that a could perform better in this instance, since one of the tables in the join is 10x smaller than the other. In order to get CockroachDB to plan a lookup join in this case, you will need to add an explicit index on the join key for the right-hand-side table, in this case, rides.
Next, you can specify the lookup join with a join hint:
The results, however, are not good. The query is much slower using a lookup join than what CockroachDB planned for you earlier. The query is faster when you force CockroachDB to use a merge join:
The results are consistently about 20-26ms with a merge join versus 16-23ms when you let CockroachDB choose the join type as shown in the previous section. In other words, forcing the merge join is slightly slower than if you had done nothing.

See also