{"id":105217,"date":"2025-02-13T22:56:00","date_gmt":"2025-02-13T22:56:00","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=105217"},"modified":"2025-03-26T20:20:22","modified_gmt":"2025-03-26T20:20:22","slug":"mysql-index-overviews-descending-b-tree-indexes","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/mysql\/mysql-index-overviews-descending-b-tree-indexes\/","title":{"rendered":"MySQL Index Overviews: Descending B-Tree Indexes"},"content":{"rendered":"\n<p>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). <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/mysql\/the-nuances-of-mysql-indexes\/\">We\u2019ve covered indexes in this blog already<\/a> \u2013 there, we\u2019ve told you that indexes come in a variety of shapes and sizes including B-tree, hash, spatial, prefix, composite, covering, and clustered indexes.<\/p>\n\n\n\n<p>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\u2019 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.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-are-descending-indexes\">What are Descending Indexes?<\/h2>\n\n\n\n<p>Descending indexes are just what they sound like, really \u2013 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 \u2013 such indexes work the same way, just the other way around: they store rows in an ascending order.<\/p>\n\n\n\n<p>Such behavior of indexes becomes very useful when our use case necessitates the use of the <code>ORDER BY<\/code> clause. Think about it \u2013 the ORDER BY clause \u201cgives an order\u201d 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 \u2013 the clause comes after our SELECT definition and looks like so:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">SELECT `col1`, `col2` <br>FROM `demo_table` \nORDER BY `id` ASC|DESC;<\/pre>\n\n\n\n<p>Such queries are nothing fancy, and indexes in action when such operations are being run don\u2019t have anything fancy within themselves either: queries will be constructed with the <code>ORDER BY<\/code> and <code>ASC<\/code> or <code>DESC<\/code> clauses (example above), while indexes will have only the <code>ASC<\/code> or <code>DESC<\/code> clauses (examples are provided below.)<\/p>\n\n\n\n<p>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\u2019t allowed to cross:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Descending indexes are supported only for the InnoDB storage engine.<\/li>\n\n\n\n<li>Data types that don\u2019t provide support for ascending indexes won\u2019t support descending indexes. At the same time, all data types that support ascending indexes will provide support for descending indexes as well.<\/li>\n\n\n\n<li>Queries that use aggregate functions like <code>MIN()<\/code>, <code>MAX()<\/code>, 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\u2019s the case because <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.4\/en\/descending-indexes.html\">according to the documentation of MySQL<\/a>, such indexes may not be used for queries that invoke aggregate functions without a <code>GROUP BY<\/code> clause.<\/li>\n\n\n\n<li>Descending indexes can only be in a B-tree \u201cshape.\u201d<\/li>\n<\/ul>\n<\/div>\n\n\n<p>Aside from that, many of the features of descending indexes overlap with B-tree indexes <em>because a descending index isn\u2019t an index type, but rather, a solution to a specific problem.<\/em> The descending part of those indexes comes into play when we invoke it, which I will quickly prove by providing examples.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-creating-descending-indexes\">Creating Descending Indexes<\/h2>\n\n\n\n<p>A descending b-tree index on a column can be defined like so:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">ALTER TABLE `demo_table` ADD INDEX `desc_idx`(`col1` DESC);<\/pre>\n\n\n\n<p>Descending or ascending indexes can also be defined on multiple columns like so:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">ALTER TABLE `demo_table` \n     ADD INDEX `desc_idx`(`col1` DESC, `col2` DESC);<\/pre>\n\n\n\n<p>One can define mixed types of indexing on multiple columns bearing different data types, too \u2013 so, <em>ascending or descending indexes work well with integers as well as with other types of data<\/em> and they can be used as partial indexes too:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">CREATE TABLE `demo_table` (\n`id` INT(6) AUTO_INCREMENT PRIMARY KEY,\n`int_column` INT(5) NOT NULL DEFAULT 0,\n    `data_column` VARCHAR(25) NOT NULL DEFAULT '',\n    INDEX `desc_idx`(\n        `int_column` DESC,\n        `data_column` ASC\n    )\n);<\/pre>\n\n\n\n<p>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 \u2013 that helps us save data on the disk):<\/p>\n\n\n<div class=\"block-core-list\">\n<ol class=\"wp-block-list\">\n<li><code>ALTER TABLE `demo_table` ADD INDEX `username_desc`(`username` DESC);<\/code><\/li>\n\n\n\n<li><code>ALTER TABLE `demo_table` ADD INDEX `partialidx`(`email`(10) DESC);<\/code><\/li>\n<\/ol>\n<\/div>\n\n\n<p>&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"941\" height=\"190\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-1.png\" alt=\"\" class=\"wp-image-105218\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-1.png 941w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-1-300x61.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-1-768x155.png 768w\" sizes=\"auto, (max-width: 941px) 100vw, 941px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>Image 1 \u2013 Descending Index and a Partial Descending Index<\/strong><\/p>\n\n\n\n<p>Once you understand that descending indexes are the same as B-tree indexes, but with data stored in descending order (\u201cin reverse\u201d, if you will), you can move on to their use cases.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-examples-and-use-cases\">Examples and Use Cases<\/h2>\n\n\n\n<p>Great \u2013 now two of our columns within a table have descending indexes on top. Let\u2019s 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:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">ALTER TABLE `demo_table` \n   ADD COLUMN `details` VARCHAR(115) NOT NULL DEFAULT '' [AFTER ...]; <\/pre>\n\n\n\n<p>Afterwards, we can start digging into the examples (for this example, our table is named <code>data_table<\/code> \u2013 the table structure is the same):<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1916\" height=\"701\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-2.png\" alt=\"\" class=\"wp-image-105219\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-2.png 1916w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-2-300x110.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-2-1024x375.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-2-768x281.png 768w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-2-1536x562.png 1536w\" sizes=\"auto, (max-width: 1916px) 100vw, 1916px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>Image 2 \u2013 Using Descending Indexes<\/strong><\/p>\n\n\n\n<p>From the examples above, we see that:<\/p>\n\n\n<div class=\"block-core-list\">\n<ol class=\"wp-block-list\">\n<li>For a descending index to be used, <em>you don\u2019t have to order by the column you have a descending index on<\/em> \u2013 the column being in a <code>WHERE<\/code> clause is enough (example #1 on both of the tables.)<\/li>\n\n\n\n<li>You can see in this example that the descending index can be used, even when the operation is specified as ascending (example #2.)<\/li>\n\n\n\n<li>If a user is searching for two columns using the <code>AND<\/code> or <code>OR<\/code> 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.)<\/li>\n<\/ol>\n<\/div>\n\n\n<p>Example #3 is the best of all because it\u2019s like having 2-3 <code>EXPLAIN<\/code> outputs mashed into one. \u201cI\u2019m using a part of the <code>username_desc<\/code> key and a part of the <code>partialdetails_desc<\/code> key and I\u2019m using the index condition, the <code>WHERE<\/code>, a filesort operation, and the <code>rowid<\/code> filter\u201d, &#8211; said MariaDB.<\/p>\n\n\n\n<p>So, how is MariaDB accessing the rows? Let\u2019s dig into the query:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">SELECT * \nFROM `data_table` \nWHERE `username` = \u2018Ralling\u2019 \n     AND `details` = \u2018Canis lupus\u2019 \nORDER BY username DESC;<\/pre>\n\n\n<div class=\"block-core-list\">\n<ol class=\"wp-block-list\">\n<li>We select every row without using any <code>UNION<\/code> operators.<\/li>\n\n\n\n<li>We\u2019re searching for a username \u2013 a column that has a descending index.<\/li>\n\n\n\n<li>In addition (<code>AND<\/code>), we\u2019re also searching for any prominent details.<\/li>\n\n\n\n<li>We\u2019re ordering by username in a descending fashion.<\/li>\n<\/ol>\n<\/div>\n\n\n<p>In other words, we tell our database that we\u2019re using a simple select type (point #1.) After that, <code>`data_table`<\/code> tells our database that we\u2019re selecting data from the table named <code>data_table<\/code>. The <code>ref|filter<\/code> data access types mean that <em>our data resides in an indexed column that\u2019s accessed by an equality operator<\/em> (that\u2019s the <code>username<\/code> column and that\u2019s what <code>ref<\/code> refers to) and <em>that a condition was applied by filtering certain rows out from consideration because a partial descending index was used<\/em> (<code>filter<\/code>.)<\/p>\n\n\n\n<p>We have two possible indexes \u2013 a descending index for username because we select the username column after the <code>WHERE<\/code> clause, and a partial descending index for the details column because we use it after the AND clause and nothing\u2019s obstructing a descending index from being accessed.<\/p>\n\n\n\n<p>The <code>key_len<\/code> column depicts the length of the first (left) and second (right) descending indexes. The <code>const<\/code> state of the <code>ref<\/code> column means that MariaDB has interpreted one matching row that it\u2019d 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 (\u201cUsing the index condition\u201d), the <code>WHERE<\/code> clause (\u201cUsing where\u201d), and placed rows on a temporary table which was sorted on the disk (\u201cUsing filesort.\u201d) Using <code>rowid<\/code> filter means that rows were filtered by the row <code>ID<\/code>.<\/p>\n\n\n\n<p>We\u2019ve successfully deconstructed a complex query. Thus, descending indexes aren\u2019t exactly rocket science \u2013 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 \u2013 then, you will <a id=\"post-105217-_heading=h.gjdgxs\"><\/a>quickly make sure that that\u2019s indeed the case.<\/p>\n\n\n\n<p>As far as examples go, we add a descending index on the id column for illustration purposes:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">ALTER TABLE `demo_table` ADD INDEX `desc_id`(`id` DESC);<\/pre>\n\n\n\n<p>For an example, I let\u2019s run a couple of queries:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">EXPLAIN SELECT * FROM `demo_table` WHERE `messages_sent` &gt; 0 AND email = 'king@demo.com' \n        ORDER BY id ASC|DESC;\n\nEXPLAIN SELECT `id` FROM `demo_table` WHERE `details` = 'Has a bad reputation' \n        ORDER BY `id` DESC;\n\nEXPLAIN SELECT `id` FROM `demo_table` WHERE `id` = 137751 \n        ORDER BY `id` DESC;\n\nEXPLAIN SELECT `id` FROM `demo_table` WHERE `messages_received` != 0 AND details != '' \n        ORDER BY `id` DESC;<\/pre>\n\n\n\n<p>These queries will return the following results:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1370\" height=\"765\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-3.png\" alt=\"\" class=\"wp-image-105220\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-3.png 1370w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-3-300x168.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-3-1024x572.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/01\/word-image-105217-3-768x429.png 768w\" sizes=\"auto, (max-width: 1370px) 100vw, 1370px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>Image 3 \u2013 More Examples of Descending Indexes<\/strong><\/p>\n\n\n<div class=\"block-core-list\">\n<ol class=\"wp-block-list\">\n<li>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. <em>That\u2019s because your database will always choose the fastest way to access data.<\/em><\/li>\n\n\n\n<li>In the second example, we see that the database hasn\u2019t found any possible indexes to use at first glance (the value of <code>possible_keys <\/code>is <code>NULL<\/code>), but the database has decided to use the primary key index as well. The reason behind that decision is also simple \u2013 since we order by id and the id is a primary key, the choice is obvious.<\/li>\n\n\n\n<li>The third example considers the use of a descending index on the <code>id<\/code> column \u2013 however, given that defining descending indexes on primary key columns is often futile (<em>scanning through a primary key column will always be faster no matter if any other kinds of indexes are defined or not<\/em>), MySQL ignores the index.<\/li>\n\n\n\n<li>The last example uses the primary key because our query works with the id column. It\u2019s an obvious choice.<\/li>\n<\/ol>\n<\/div>\n\n\n<p>Hopefully, these examples should enable you to better understand how your database thinks (and acts) before deciding in these cases.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-summary\">Summary<\/h2>\n\n\n\n<p>Descending indexes are B-tree indexes that store data in a descending order. Descending indexes aren\u2019t 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 \u2026 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 \u201cgotchas\u201d you need to follow for your work with them not to become a nightmare.<\/p>\n\n\n\n<p>Regardless, both descending and ascending indexes have a place in your database \u2013 think of them as B-tree indexes with a twist.<\/p>\n\n\n\n<p>Descending Indexes \u2013 Frequently Asked Questions<\/p>\n\n\n\n<p><em>Q: What is a descending index?<br>A: A descending index is an index with its values stored in a descending order, hence the name.<\/em><\/p>\n\n\n\n<p><em>Q: How and when to define a descending index?<br>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.<\/em><\/p>\n\n\n\n<p><em>Q: Do descending indexes have a performance penalty?<br>A: No. MySQL is said to have been penalizing queries that scanned indexes in reverse order in the past, but that\u2019s no longer the case.<\/em><\/p>\n\n\n\n<p><em>Q: Where can I learn more about descending indexes and other database secrets?<br>A: Follow <\/em><a href=\"https:\/\/www.red-gate.com\/simple-talk\/articles\/\"><em>the Red-gate Simple Talk blog<\/em><\/a><em>, <\/em><a href=\"https:\/\/hackingmysql.com\"><em>read books<\/em><\/a><em>, attend industry workshops and conferences, and consider subscribing to <\/em><a href=\"https:\/\/databasedive.com\"><em>the YouTube channel Database Dive.<\/em><\/a><\/p>\n\n\n\n<p>Appendix \u2013 Table Structure &amp; Data<\/p>\n\n\n\n<p>Table structure and data in this blog were generated by a <a href=\"https:\/\/www.mockaroo.com\/\">demo data generator Mockaroo<\/a>. The table structure and data can be found <a href=\"https:\/\/pastebin.com\/t1G2z8sf\">here<\/a> (the example data set contains slightly over 1,300 rows \u2013 if necessary, you can generate more by making use of the table structure and using the demo data generator.)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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\u2019ve covered indexes&#8230;&hellip;<\/p>\n","protected":false},"author":339547,"featured_media":105228,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[53,145792],"tags":[5854],"coauthors":[146040],"class_list":["post-105217","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-featured","category-mysql","tag-mysql"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/105217","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/339547"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=105217"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/105217\/revisions"}],"predecessor-version":[{"id":105227,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/105217\/revisions\/105227"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/105228"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=105217"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=105217"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=105217"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=105217"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}