Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use specific mysql index with rails

I have this ActiveRecord query

issue = Issue.find(id)
issue.articles.includes(:category).merge(Category.where(permalink: perma))

And the translated to mysql query

SELECT `articles`.`id` AS t0_r0, `articles`.`title` AS t0_r1, 
       `articles`.`hypertitle` AS t0_r2, `articles`.`html` AS t0_r3,
       `articles`.`author` AS t0_r4, `articles`.`published` AS t0_r5,
       `articles`.`category_id` AS t0_r6, `articles`.`issue_id` AS t0_r7,
       `articles`.`date` AS t0_r8, `articles`.`created_at` AS t0_r9, 
       `articles`.`updated_at` AS t0_r10, `articles`.`photo_file_name` AS t0_r11,
       `articles`.`photo_content_type` AS t0_r12, `articles`.`photo_file_size` AS t0_r13,
       `articles`.`photo_updated_at` AS t0_r14, `categories`.`id` AS t1_r0,
       `categories`.`name` AS t1_r1, `categories`.`permalink` AS t1_r2,
       `categories`.`created_at` AS t1_r3, `categories`.`updated_at` AS t1_r4,
       `categories`.`issued` AS t1_r5, `categories`.`order_articles` AS t1_r6 
        FROM `articles` LEFT OUTER JOIN `categories` ON 
       `categories`.`id` = `articles`.`category_id` WHERE 
       `articles`.`issue_id` = 409 AND `categories`.`permalink` = 'Διεθνή' LIMIT 1

In the explation of this query I saw that uses wrong index

+----+-------------+------------+-------+---------------------------------------------------------------------------+-------------------------------+---------+-------+------+----------+-------------+
| id | select_type | table      | type  | possible_keys                                                             | key                           | key_len | ref   | rows | filtered | Extra       |
+----+-------------+------------+-------+---------------------------------------------------------------------------+-------------------------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | categories | const | PRIMARY,index_categories_on_permalink                                     | index_categories_on_permalink | 768     | const |    1 |   100.00 |             |
|  1 | SIMPLE      | articles   | ref   | index_articles_on_issue_id_and_category_id, index_articles_on_category_id | index_articles_on_category_id | 2       | const |   10 |   100.05 | Using where |
+----+-------------+------------+-------+---------------------------------------------------------------------------+-------------------------------+---------+-------+------+----------+-------------+

I have two indexes, category_id alone and issue_id - category_id.

In this query I'm searching with issue_id and category_id which is much faster when using the index_articles_on_issue_id_and_category_id than the index_articles_on_category_id.

How can I select the correct index with active record query?

like image 737
mike zaby Avatar asked Dec 16 '12 18:12

mike zaby


People also ask

How do I select an index in MySQL?

To see the index for a specific table use SHOW INDEX: SHOW INDEX FROM yourtable; To see indexes for all tables within a specific schema you can use the STATISTICS table from INFORMATION_SCHEMA: SELECT DISTINCT TABLE_NAME, INDEX_NAME FROM INFORMATION_SCHEMA.

What is use of index in rails?

An index is used to speed up the performance of queries on a database. Rails allows us to create index on a database column by means of a migration. By default, the sort order for the index is ascending. But consider the case where we are fetching reports from the database.

Does MySQL use index automatically?

Mostly we create index when creating table. Any column in creating table statement declared as PRIMARY KEY, KEY, UNIQUE or INDEX will be indexed automatically by MySQL. In addition, you can add indexes to the tables which has data.

Does MySQL use index for sorting?

Yes, MySQL uses your index to sort the information when the order is by the sorted column. Also, if you have indexes in all columns that you have added to the SELECT clause, MySQL will not load the data from the table itself, but from the index (which is faster).


2 Answers

You can facilitate arel like so to use an index:

 class Issue
   def self.use_index(index)
     # update: OP fixed my mistake
     from("#{self.table_name} USE INDEX(#{index})")
   end
 end

 # then
 Issue.use_index("bla").where(some_condition: true)
like image 172
krichard Avatar answered Sep 18 '22 15:09

krichard


Add use_index to ActiveRecord::Relation.

There was some discussion for multiple years about adding this to Rails core, however, it looks like the PR and branch got abandoned.

If you are aware of what database you're using and the limitations of this, you can easily add this to your own codebase so it can be used.

Very similar to @krichard's solution except more generalized for all models to use instead of just Issue.

config/initializers/active_record_relation.rb

class ActiveRecord::Relation
  # Allow passing index hints to MySQL in case the query planner gets confused.
  #
  # Example:
  #   Message.first.events.use_index( :index_events_on_eventable_type_and_eventable_id )
  #   #=> Event Load (0.5ms)  SELECT `events`.* FROM `events` USE INDEX (index_events_on_eventable_type_and_eventable_id) WHERE `events`.`eventable_id` = 123 AND `events`.`eventable_type` = 'Message'
  #
  # MySQL documentation:
  #    https://dev.mysql.com/doc/refman/5.7/en/index-hints.html
  #
  # See https://github.com/rails/rails/pull/30514
  #
  def use_index( index_name )
    self.from( "#{ self.quoted_table_name } USE INDEX ( #{ index_name } )" )
  end
end

This will allow you to use something like:

issue.articles.includes( :category ).use_index( :index_articles_on_issue_id_and_category_id )

And the resulting SQL will include:

FROM articles USE INDEX( index_articles_on_issue_id_and_category_id )
like image 38
Joshua Pinter Avatar answered Sep 18 '22 15:09

Joshua Pinter