Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple indexes on one column

Using Oracle, there is a table called User.

Columns: Id, FirstName, LastName

Indexes: 1. PK(Id), 2. UPPER(FirstName), 3. LOWER(FirstName), 4. Index(FirstName)

As you can see index 2, 3, 4 are indexes on the same column - FirstName.

I know this creates overhead, but my question is on selecting how will the database react/optimize?

For instance:

SELECT Id FROM User u WHERE u.FirstName LIKE 'MIKE%'

Will Oracle hit the right index or will it not?

The problem is that via Hibernate this slows down the query VERY much (so it uses prepared statements).

Thanks.

UPDATE: Just to clarify indexes 2 and 3 are functional indexes.

like image 800
Alexandru Luchian Avatar asked Mar 08 '11 21:03

Alexandru Luchian


3 Answers

In addition to Mat's point that either index 2 or 3 should be redundant because you should choose one approach to doing case-insensitive searches and to Richard's point that it will depend on the selectivity of the index, be aware that there are additional concerns when you are using the LIKE clause.

Assuming you are using bind variables (which it sounds like you are based on your use of prepared statements), the optimizer has to guess at how selective the actual bind value is going to be. Something short like 'S%' is going to be very non-selective, causing the optimizer to generally prefer a table scan. A longer string like 'Smithfield-Manning%', on the other hand, is likely to be very selective and would likely use index 4. How Oracle handles this variability will depend on the version.

In Oracle 10, Oracle introduced bind variable peeking. This meant that the first time Oracle parsed a query after a reboot (or after the query plan being aged out of the shared pool), Oracle looked at the bind value and decided what plan to use based on that value. Assuming that most of your queries would benefit from the index scan because users are generally searching on relatively selective values, this was great if the first query after a reboot had a selective condition. But if you got unlucky and someone did a WHERE firstname LIKE 'S%' immediately after a reboot, you'd be stuck with the table scan query plan until the query plan was removed from the shared pool.

Starting in Oracle 11, however, the optimizer has the ability to do adaptive cursor sharing. That means that the optimizer will try to figure out that WHERE firstname LIKE 'S%' should do a table scan and WHERE firstname LIKE 'Smithfield-Manning%' should do an index scan and will maintain multiple query plans for the same statement in the shared pool. That solves most of the problems that we had with bind variable peeking in earlier versions.

But even here, the accuracy of the optimizer's selectivity estimates are generally going to be problematic for medium-length strings. It's generally going to know that a single-character string is very weakly selective and that a 20 character string is highly selective but even with a 256 bucket histogram, it's not going to have a whole lot of information about how selective something like WHERE firstname LIKE 'Smit%' really is. It may know roughly how selective 'Sm%' is based on the column histogram but it's guessing rather blindly at how selective the next two characters are. So it's not uncommon to end up in a situation where most of the queries work efficiently but the optimizer is convinced that WHERE firstname LIKE 'Cave%' isn't selective enough to use an index.

Assuming that this is a common query, you may want to consider using Oracle's plan stability features to force Oracle to use a particular plan regardless of the value of a bind variable. This may mean that users that enter a single character have to wait even longer than they would otherwise have waited because the index scan is substantially less efficient than doing a table scan. But that may be worth it for other users that are searching for short but reasonably distinctive last names. And you may do things like add a ROWNUM limiter to the query or add logic to the front end that requires a minimum number of characters in the search box to avoid situations where a table scan would be more efficient.

like image 124
Justin Cave Avatar answered Oct 14 '22 20:10

Justin Cave


It's a bit strange to have both the upper and lower function-based indexes on the same field. And I don't think the optimizer will use either in your query as it its.

You should pick one or the other (and probably drop the last one too), and only ever query on the upper (or lower)-case with something like:

select id from user u where upper(u.firstname) like 'MIKE%'

Edit: look at this post too, has some interesting info How to use a function-based index on a column that contains NULLs in Oracle 10+?

like image 29
Mat Avatar answered Oct 14 '22 20:10

Mat


It may not hit any of your indexes, because you are returning ID in the SELECT clause, which is not covered by the indexes.

If the index is very selective, and Oracle decides it is still worthwhile using it to find 'MIKE%' then perform a lookup on the data to get the ID column, then it may use 4. Index(FirstName). 2 and 3 will only be used if the column searched uses the exact function defined in the index.

like image 44
RichardTheKiwi Avatar answered Oct 14 '22 22:10

RichardTheKiwi