Most anyone who works with indexes should be familiar with B-tree indexes. These are the most commonly used data structures to facilitate faster access to data when we search for exact matches of data using the WHERE clause (or partial matches that can take advantage of the sort order of the index). We’ve covered indexes in this blog already – there, we’ve told you that indexes come in a variety of shapes and sizes including B-tree, hash, spatial, prefix, composite, covering, and clustered indexes.
Some readers of this blog may also know that B-tree indexes have a couple of characteristics unique to themselves: they can also be of an ascending or descending order. In this article, we will look at why you might choose to set an index’ order from the default of ascending for all the key values. Some readers of this blog may also know that B-tree indexes have a couple of characteristics unique to themselves: they can also be of an ascending or descending descent, and this is precisely what this blog is about.
What are Descending Indexes?
Descending indexes are just what they sound like, really – they are B-tree indexes that store rows in a descending order based on the index key configuration. The same can be said about ascending indexes, too – such indexes work the same way, just the other way around: they store rows in an ascending order.
Such behavior of indexes becomes very useful when our use case necessitates the use of the ORDER BY
clause. Think about it – the ORDER BY clause “gives an order” to our database so that the database sorts the data in a certain way, In other words, the order of rows becomes important once we find ourselves running queries with the ORDER BY clause – the clause comes after our SELECT definition and looks like so:
SELECT `col1`, `col2`
FROM `demo_table` ORDER BY `id` ASC|DESC;
Such queries are nothing fancy, and indexes in action when such operations are being run don’t have anything fancy within themselves either: queries will be constructed with the ORDER BY
and ASC
or DESC
clauses (example above), while indexes will have only the ASC
or DESC
clauses (examples are provided below.)
Both ascending and descending index keys are supported in many database management systems, including MySQL and all of its flavors. As such, descending indexes have limitations and conditions that mark lines they aren’t allowed to cross:
- Descending indexes are supported only for the InnoDB storage engine.
- Data types that don’t provide support for ascending indexes won’t support descending indexes. At the same time, all data types that support ascending indexes will provide support for descending indexes as well.
- Queries that use aggregate functions like
MIN()
,MAX()
, or the like, but do not use the GROUP BY clause may not make use of a descending index (but may make use of other types of indexes instead.) That’s the case because according to the documentation of MySQL, such indexes may not be used for queries that invoke aggregate functions without aGROUP BY
clause. - Descending indexes can only be in a B-tree “shape.”
Aside from that, many of the features of descending indexes overlap with B-tree indexes because a descending index isn’t an index type, but rather, a solution to a specific problem. The descending part of those indexes comes into play when we invoke it, which I will quickly prove by providing examples.
Creating Descending Indexes
A descending b-tree index on a column can be defined like so:
ALTER TABLE `demo_table` ADD INDEX `desc_idx`(`col1` DESC);
Descending or ascending indexes can also be defined on multiple columns like so:
ALTER TABLE `demo_table` ADD INDEX `desc_idx`(`col1` DESC, `col2` DESC);
One can define mixed types of indexing on multiple columns bearing different data types, too – so, ascending or descending indexes work well with integers as well as with other types of data and they can be used as partial indexes too:
CREATE TABLE `demo_table` ( `id` INT(6) AUTO_INCREMENT PRIMARY KEY, `int_column` INT(5) NOT NULL DEFAULT 0, `data_column` VARCHAR(25) NOT NULL DEFAULT '', INDEX `desc_idx`( `int_column` DESC, `data_column` ASC ) );
When altering tables, descending indexes on a column can be defined like so (we define a partial descending index (meaning it only uses the first (N) characters from the string index key) in the second example – that helps us save data on the disk):
ALTER TABLE `demo_table` ADD INDEX `username_desc`(`username` DESC);
ALTER TABLE `demo_table` ADD INDEX `partialidx`(`email`(10) DESC);

Image 1 – Descending Index and a Partial Descending Index
Once you understand that descending indexes are the same as B-tree indexes, but with data stored in descending order (“in reverse”, if you will), you can move on to their use cases.
Examples and Use Cases
Great – now two of our columns within a table have descending indexes on top. Let’s add more details column and make use of them. We add a column by specifying its name, data type, and whether we want the column to be positioned after another column like so:
ALTER TABLE `demo_table` ADD COLUMN `details` VARCHAR(115) NOT NULL DEFAULT '' [AFTER ...];
Afterwards, we can start digging into the examples (for this example, our table is named data_table
– the table structure is the same):

Image 2 – Using Descending Indexes
From the examples above, we see that:
- For a descending index to be used, you don’t have to order by the column you have a descending index on – the column being in a
WHERE
clause is enough (example #1 on both of the tables.) - You can see in this example that the descending index can be used, even when the operation is specified as ascending (example #2.)
- If a user is searching for two columns using the
AND
orOR
clauses, one of which has a descending index and the other has a partial descending index, both types of indexes can be used (example #3.)
Example #3 is the best of all because it’s like having 2-3 EXPLAIN
outputs mashed into one. “I’m using a part of the username_desc
key and a part of the partialdetails_desc
key and I’m using the index condition, the WHERE
, a filesort operation, and the rowid
filter”, – said MariaDB.
So, how is MariaDB accessing the rows? Let’s dig into the query:
SELECT * FROM `data_table` WHERE `username` = ‘Ralling’ AND `details` = ‘Canis lupus’ ORDER BY username DESC;
- We select every row without using any
UNION
operators. - We’re searching for a username – a column that has a descending index.
- In addition (
AND
), we’re also searching for any prominent details. - We’re ordering by username in a descending fashion.
In other words, we tell our database that we’re using a simple select type (point #1.) After that, `data_table`
tells our database that we’re selecting data from the table named data_table
. The ref|filter
data access types mean that our data resides in an indexed column that’s accessed by an equality operator (that’s the username
column and that’s what ref
refers to) and that a condition was applied by filtering certain rows out from consideration because a partial descending index was used (filter
.)
We have two possible indexes – a descending index for username because we select the username column after the WHERE
clause, and a partial descending index for the details column because we use it after the AND clause and nothing’s obstructing a descending index from being accessed.
The key_len
column depicts the length of the first (left) and second (right) descending indexes. The const
state of the ref
column means that MariaDB has interpreted one matching row that it’d read at the beginning of the query and is interpreting the values from the columns associated with that row as constants, and the 3 conditions mean that our database has used the index (“Using the index condition”), the WHERE
clause (“Using where”), and placed rows on a temporary table which was sorted on the disk (“Using filesort.”) Using rowid
filter means that rows were filtered by the row ID
.
We’ve successfully deconstructed a complex query. Thus, descending indexes aren’t exactly rocket science – they just have a couple of things to be mindful of. Now, we recommend that you look at the illustration below and then walk yourself through some more examples – then, you will quickly make sure that that’s indeed the case.
As far as examples go, we add a descending index on the id column for illustration purposes:
ALTER TABLE `demo_table` ADD INDEX `desc_id`(`id` DESC);
For an example, I let’s run a couple of queries:
EXPLAIN SELECT * FROM `demo_table` WHERE `messages_sent` > 0 AND email = 'king@demo.com' ORDER BY id ASC|DESC; EXPLAIN SELECT `id` FROM `demo_table` WHERE `details` = 'Has a bad reputation' ORDER BY `id` DESC; EXPLAIN SELECT `id` FROM `demo_table` WHERE `id` = 137751 ORDER BY `id` DESC; EXPLAIN SELECT `id` FROM `demo_table` WHERE `messages_received` != 0 AND details != '' ORDER BY `id` DESC;
These queries will return the following results:

Image 3 – More Examples of Descending Indexes
- In the first example, we see that our database has considered using the partial index because the ascending operation was specified but decided to go with the primary index instead. That’s because your database will always choose the fastest way to access data.
- In the second example, we see that the database hasn’t found any possible indexes to use at first glance (the value of
possible_keys
isNULL
), but the database has decided to use the primary key index as well. The reason behind that decision is also simple – since we order by id and the id is a primary key, the choice is obvious. - The third example considers the use of a descending index on the
id
column – however, given that defining descending indexes on primary key columns is often futile (scanning through a primary key column will always be faster no matter if any other kinds of indexes are defined or not), MySQL ignores the index. - The last example uses the primary key because our query works with the id column. It’s an obvious choice.
Hopefully, these examples should enable you to better understand how your database thinks (and acts) before deciding in these cases.
Summary
Descending indexes are B-tree indexes that store data in a descending order. Descending indexes aren’t a distinct index type, but rather, a use case of B-tree indexes and contrary to popular belief, such a type of index can even be used when the ORDER BY … DESC clause is not in use (i.e. descending indexes can work as regular B-tree indexes because they act the same), but such indexes do have a couple of “gotchas” you need to follow for your work with them not to become a nightmare.
Regardless, both descending and ascending indexes have a place in your database – think of them as B-tree indexes with a twist.
Descending Indexes – Frequently Asked Questions
Q: What is a descending index?
A: A descending index is an index with its values stored in a descending order, hence the name.
Q: How and when to define a descending index?
A: To define a descending index, append the DESC keyword when defining the index. Consider defining a descending index on a column when you run queries with the DESC keyword.
Q: Do descending indexes have a performance penalty?
A: No. MySQL is said to have been penalizing queries that scanned indexes in reverse order in the past, but that’s no longer the case.
Q: Where can I learn more about descending indexes and other database secrets?
A: Follow the Red-gate Simple Talk blog, read books, attend industry workshops and conferences, and consider subscribing to the YouTube channel Database Dive.
Appendix – Table Structure & Data
Table structure and data in this blog were generated by a demo data generator Mockaroo. The table structure and data can be found here (the example data set contains slightly over 1,300 rows – if necessary, you can generate more by making use of the table structure and using the demo data generator.)
The post MySQL Index Overviews: Descending B-Tree Indexes appeared first on Simple Talk.