Loosely speaking, the CBO may choose to:
- build up a list of all possible values for the 'missing' leading columns (this can be done fairly efficiently from the index structure itself)
- iteratively perform range scans for each combination of missing columns and the column provided
- union the whole lot together in one result set
This is what is called a 'skip scan' in Oracle terminology. Skip scans work best when the number of possible values in step (1) is relatively small (that is small compared to the size of the index)
Under what circumstances can Oracle (at least in 11g) do a lookup
without the left-most prefix columns existing in the query?
Oracle will use statistics to get an estimate of the cardinality of step (1) before weighing up if performing that many range scans will cost more than just scanning the whole index sequentially
What has to be adhered to is the query you know you are going to make.
Let go back to the original question's query
SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM blogposts
JOIN articles ON articles.blogpost_id = blogposts.id
WHERE blogposts.deleted = 0
AND blogposts.title LIKE '%{de}%'
AND blogposts.visible = 1
AND blogposts.date_published <= NOW()
ORDER BY blogposts.date_created DESC
LIMIT 0 , 50
Look carefully at the WHERE clause. I see two static values
blogposts.deleted = 0
blogposts.visible = 1
These cannot be indexes by themselves because of their implied cardinality. Think about it:
deleted
is either 0 or 1 (That's a Cardinality of 2)
visible
is either 0 or 1 (That's a Cardinality of 2)
All Query Optimizers (MySQL, Oracle, PostgreSQL, MSSQL, etc) would take one look at those indexes and decide not to use the index.
Look at the ORDER BY
clause : ORDER BY blogposts.date_created DESC
. This shows a predictable ordering schema. This may help bypass the need for sorting.
Combining these three columns into a single index gives the Query Optimizer some relief by gathering data with delete=0,visible=1,date_created already merged.
Although MySQL is perfectly capable of doing Index Merging, you should never expect the Query Optimizer to do index merging on indexes with very low cardinalities. Even if it did, just expect poor performance. Query Optimizers will choose full table scans, full index scans, and range scans over lopsided index merges any day. Creating covering indexes can bypass having to do index merges, but it can become a burden or a big waste of time and diskspace if the cardinality of the multiple-column index is still too low. Thus, the Query Optimizer would still choose not use it. Therefore, you must know your key distribution well in order to formulate multiple column indexes that you will write queries to use properly.
Here are some nice Links on Covering Indexes and When to Make Them
As far the date_published
column goes, it would be alright to add it to the index. It would bring that much more relief to the Query Optimizer. It will perform an index scan to test for <=NOW()
but that OK. That's will covering indexes are for.
Best Answer
Looking at the MySQL Documentation, the glossary indicates this about Covering Indexes:
The implication here is that there is no direct equivalent of an
INCLUDE
statement in MySQL, however if the index provides coverage of the columns involved in an index, it can, rather obviously, still be considered to be covering, much like an index with included columns in SQL Server would be.MariaDB has the following in their documentation for covering indexes: