2
\$\begingroup\$

I have the following query which brings back a list of items which fall into a particular category:

SELECT i.id, ir.range_name, it.item_type, i.item_value
FROM items i
LEFT JOIN item_types it ON it.id = i.item_type
LEFT JOIN item_ranges ir ON ir.id = i.item_name
LEFT JOIN menu m ON m.id = it.menu_link
WHERE m.menu_title =  'vehicles'
AND i.item_live = 1
ORDER BY ir.range_name

I was wondering whether the m.menu_title column needs to be indexed. Currently, there are only about 10 rows in the menu (m) table and this is unlikely to change that much.

However, with the joins, the above query will potentially bring back over 1000000 rows. There are foreign keys on all the joins.

I am trying to avoid filling the database with dummy data to test the query.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ If you're looking to optimize the query, a good place to start would be inspecting the output of EXPLAIN SELECT. Also, with the proper indexes, it should be no problem to have the database return the results in the desired order. \$\endgroup\$ Commented Sep 5, 2014 at 18:46

2 Answers 2

5
\$\begingroup\$

Yes, No, and Maybe.

Yes

The table has 10 rows, the column is used as a constant argument in a query, and the query optimizer has ways to do index lookups (probes) in a very fast way. So, if the query parser decides to access the menu table by the literal, instead of the id, then the index will likely help. Also, for 10 rows, it will not take much space.

No

Because you use a left-join from items to menu, on the menu.id column, the reality is that the query plan will likely have to process all the records in item before joining to menu so that it can do the outer-join right. This means that all the query access to the menu table should be through the id column, and the menu_title column will be secondary, and the index on the menu_title (if any) will be ignored.

Maybe

Query optimizers can sometimes make dumb, and also very smart choices, and there is a chance that having the index will help, despite my best-guess that it wont. Additionally, there is a chance the optimizer will use the index, even though that will make it slower.

There is no way to know unless you try it.

My suggestion

Create the index, but don't expect it to be used for this query.

A second suggestion is to create the index menu(id, menu_title) because sometimes putting them together in an index helps avoid a data page lookup, but for just 10 rows, it's not going to make much difference.

\$\endgroup\$
6
  • \$\begingroup\$ OK, so I added 400000 rows into the item_ranges table and 1500000 to the items table and ran the query multiple times both with and without the index. My only observations are that it got progressively quicker the more times I ran the query and there was very little in it in terms of time taken to execute. However, because the times varied so much I wouldn't like to say how accurate this is. Any thoughts? \$\endgroup\$
    – Tom
    Commented Sep 5, 2014 at 14:46
  • 1
    \$\begingroup\$ The performance for that many records is almost entirely due to the IO and other needs on the other two tables (items and item_ranges) I am sure. The OrderBy will likely also need to create a fair amount of sort-space to contain the results, which need sorting before returning. Those aspects will dwarf the cost of the Indexing on menu. \$\endgroup\$
    – rolfl
    Commented Sep 5, 2014 at 14:48
  • \$\begingroup\$ You're right! The ordering makes the query 10 time longer to run. Perhaps I'll look at ordering the results in php rather than in the query. Do you have any other tips? \$\endgroup\$
    – Tom
    Commented Sep 5, 2014 at 14:57
  • \$\begingroup\$ Tom - @200_success is right that your left-join to menu can be written as an Inner-join, which will likely mean a potentially different query plan with an index. Try it and see. \$\endgroup\$
    – rolfl
    Commented Sep 5, 2014 at 15:53
  • \$\begingroup\$ Hmmmmm, changing the left join to an inner join makes no difference at all. Would you expect that? \$\endgroup\$
    – Tom
    Commented Sep 5, 2014 at 15:57
3
\$\begingroup\$

If you require menu_title = 'vehicles', then that should be written as an inner join, not a left join — for clarity and possibly a tiny performance boost.

\$\endgroup\$
4
  • \$\begingroup\$ Inner join will change the whole meaning of the query, right? (I assume the outer-join is for a reason...?) \$\endgroup\$
    – rolfl
    Commented Sep 5, 2014 at 15:29
  • \$\begingroup\$ @rolfl If the menu title filter were a join condition instead of a where condition, you would be right. \$\endgroup\$ Commented Sep 5, 2014 at 15:31
  • \$\begingroup\$ FYI: sqlfiddle.com/#!2/ec5bf/7/0 - and thanks, learned something. \$\endgroup\$
    – rolfl
    Commented Sep 5, 2014 at 15:51
  • \$\begingroup\$ I don't need menu_title in my results as such, I just need results where the item_type is in that menu item (I hope that makes sense). \$\endgroup\$
    – Tom
    Commented Sep 5, 2014 at 15:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.