そのクエリ、ちゃんとチューニングされてる？Railsで確認する方法

クエリのパフォーマンスチェックにMySQLの`EXPLAIN`を使っている方も多いと思います（私もその一人です）。
RailsコードとMySQLの行き来が多く、時間がかかります。
もっと、開発スピードもあげたいものです・・・！😩

# 今回使うテーブル
- Postsテーブル

|名前  |型  |制約  |
|---|---|---|
|id  | AUTO INCREMENT |NOT NULL ( ※ プライマリーキーなので、INDEXあり)  |
|title  | VARCHAR  | -  |
|content  | TEXT  | -  |
|user_id  | INTEGER | NOT NULL, INDEX  |



# よくやる方法（Rails ←→ MySQL）
- Railsで、該当のモデルで`.to_sql`し、SQLクエリを発行する

```rb
user_ids = [1, 2, 3]
Posts.where(user_id: user_ids).to_sql

=> SELECT `posts`.* FROM `posts` WHERE `posts`.`person_id` IN (1, 2, 3)
```
- MySQLに何らかの方法でログインする
   - ご自由に！
     - コンソール
     - GUI
- クエリの先頭に`EXPLAIN`をつけて実行

```sql
mysql> EXPLAIN SELECT `posts`.* FROM `posts` WHERE `posts`.`person_id` IN (1, 2, 3)

+----+-------------+-------------+------+--------------------------------+------+---------+------+------+-------------+
| id | select_type | table       | type | possible_keys                  | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------------+------+--------------------------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | posts       | ALL  | index_posts_on_user_id         | NULL | NULL    | NULL | 1234 | Using where |
+----+-------------+-------------+------+--------------------------------+------+---------+------+------+-------------+
1 row in set (0.01 sec)
```

この結果を見て、チューニング。
RailsコードとMySQLの行き来が発生します。
非常に手間ですね😇

# Railsだけで完結させる方法
あまり知られていないようですが、Railsでは**MySQLのEXPLAINを実行する`.explain`メソッドを提供**しています。
（私もおとといくらいにRailsのコードを読んでいて気づきました。）

```rb:activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter
# ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#explain
# https://github.com/rails/rails/blob/8b69e32412cc2867b5fdd9a33cf4e4e759057e95/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L178

def explain(arel, binds = [])
  # クエリの先頭に「EXPLAIN」をつける
  sql     = "EXPLAIN #{to_sql(arel, binds)}"
  start   = Time.now
  # クエリを実行する
  result  = exec_query(sql, "EXPLAIN", binds)
  elapsed = Time.now - start

  MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
end
```

Railsで、該当のモデルで`.explain`する

```rb
user_ids = [1, 2, 3]
Posts.where(user_id: user_ids).explain

=> EXPLAIN for: SELECT `posts`.* FROM `posts` WHERE `posts`.`person_id` IN (1, 2, 3)
+----+-------------+-------------+------+--------------------------------+------+---------+------+------+-------------+
| id | select_type | table       | type | possible_keys                  | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------------+------+--------------------------------+------+----!-----+------+------+-------------+
|  1 | SIMPLE      | posts       | ALL  | index_posts_on_user_id         | NULL | NULL    | NULL | 1234 | Using where |
+----+-------------+-------------+------+--------------------------------+------+---------+------+------+-------------+
1 row in set (0.01 sec)
```

`explain`メソッドで返ってくる結果は、MySQLで表示されているものと同じなのです。

# EXPLAINの読み方
読み方に関しては、Qiitaやブログでまとめられているので、あえて書きません。
オススメは、下記のサイトがわかりやすいです。

- [MySQLのEXPLAINを徹底解説!!](http://nippondanji.blogspot.jp/2009/03/mysqlexplain.html)

参考にどうぞ！


# まとめ
今までMySQLで確認していたことが、Rails内で完結できるって便利ですよね。
これで、パフォーマンスチューニングがはかどりそうです。

### 読んだコード

- [ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#explain](https://github.com/rails/rails/blob/8b69e32412cc2867b5fdd9a33cf4e4e759057e95/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb#L178)