1. HAZI

    No comment

    HAZI
Changes in body
Source | HTML | Preview
@@ -1,89 +1,89 @@
MySQL を使った Rails で
```ruby
Model.where(created_at: 1.yea.ago..1.day.ago).order(:id).last
```
などとすると、MySQL の仕様で `created_at` の INDEX ではなく、`PRIMARY` を使ってしまってものすごく遅くなることがあった。
調べてみたところ、どうやら自分で拡張するしかないようなので書いたという話です。
## Concern
ググると類似コードが出てきますが、違いは下記の通り
* カラム名で指定可能
* 複合インデックスも`[カラム名, カラム名]`で指定可能
* 普通にインデックス名もok
```ruby:app/models/concerns/index_hint.rb
module IndexHint
extend ActiveSupport::Concern
class_methods do
def convert_to_index_name_in_case_of_column_name(*names)
names.map { |idx| find_index_name_by_colomun_names(idx) || idx }
end
private
def find_index_name_by_colomun_names(column_names)
column_names = Array.wrap(column_names).map(&:to_s)
- name = connection.indexes(self.table_name).find { |index| index.columns == column_names }&.name
+ name = connection.indexes(table_name).find { |index| index.columns == column_names }&.name
connection.quote_column_name(name) if name
end
end
included do
scope :use_index, lambda { |*indexes|
index_names = convert_to_index_name_in_case_of_column_name(*indexes)
- from("#{self.quoted_table_name} USE INDEX(#{index_names.join(', ')})")
+ from("#{quoted_table_name} USE INDEX(#{index_names.join(', ')})")
}
scope :ignore_index, lambda { |*indexes|
index_names = convert_to_index_name_in_case_of_column_name(*indexes)
- from("#{self.quoted_table_name} IGNORE INDEX(#{index_names.join(', ')})")
+ from("#{quoted_table_name} IGNORE INDEX(#{index_names.join(', ')})")
}
scope :force_index, lambda { |*indexes|
index_names = convert_to_index_name_in_case_of_column_name(*indexes)
- from("#{self.quoted_table_name} FORCE INDEX(#{index_names.join(', ')})")
+ from("#{quoted_table_name} FORCE INDEX(#{index_names.join(', ')})")
}
end
end
```
## Use
`ApplicationRecord` レベルで `include` してあげれば全てのモデルで使えますね。
モデル単体で `include` してもokです。
```ruby:app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
include IndexHint
...
```
実際に使うとこんな感じ
```ruby
Model.use_index(:created_at).to_sql
#=> "SELECT `models`.* FROM `models` USE INDEX(`index_models_on_created_at`)"
Model.force_index([:section, :name]).to_sql
#=> "SELECT `models`.* FROM `models` FORCE INDEX(`index_models_on_section_and_name`)"
Model.ignore_index([:section, :name], :created_at).to_sql
#=> "SELECT `models`.* FROM `models` IGNORE INDEX(`index_models_on_section_and_name`, `index_models_on_created_at`)"
```
## 注意点
* `from`, `use_index`, `force_index`, `ignore_index` を組み合わせられない
## 参考
* [\[Feature\] Active Record: Add \`use\_index\` by gaurish · Pull Request \#30514 · rails/rails](https://github.com/rails/rails/pull/30514)
* [MySQL :: MySQL 5\.6 リファレンスマニュアル :: 13\.2\.9\.3 インデックスヒントの構文](https://dev.mysql.com/doc/refman/5.6/ja/index-hints.html)
* [ruby on rails & mysql use force index \- Qiita](https://qiita.com/cut_yocchan_ika/items/98280f0b91b3af0aef84)