Madogiwa Blog

主に技術系の学習メモに使っていきます。

`ActiveRecord::Relation#explain`を拡張してJSON形式のEXPLAIN結果を取得出来るgemを作った💎

前に下記の記事で紹介したActiveRecord::Relation#explainJSON形式を取得出来るモンキーパッチを紹介したのですが、 今回はそれをGemとして公開したのでそれについて書きます💎✨

madogiwa0124.hatenablog.com

Gemはactive_record_json_explainとしてRubyGems.orgに公開してます!

公開したGemのurlは下記です🙌

rubygems.org

コードはこちらです🐙

github.com

導入方法

導入方法は簡単で下記のようにGemfileに追加して、

gem 'active_record_json_explain', require: false

有効化したい箇所でrequireしてください🙋

require 'active_record_json_explain'

require後からgemで定義しているモンキーパッチが有効になります🐵
※既存のexplainには影響しない実装にしています。

使い方

出来ることは前回のブログにも記載していますが、ActiveRecord::Relation#explainの引数にjson: trueを渡すをJSON形式でEXPLAINの結果を取得出来ます👍

以下が実際にMySQLを使っている場合の結果になります🐬

$ require 'active_record_json_explain'  # モンキーパッチの有効化
=> true
$ Sample.with_title.explain(json: true)
=> EXPLAIN for: SELECT `samples`.* FROM `samples` WHERE `samples`.`title` = 'hoge'
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| {
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "0.35"
    },
    "table": {
      "table_name": "samples",
      "access_type": "ALL",
      "rows_examined_per_scan": 1,
      "rows_produced_per_join": 1,
      "filtered": "100.00",
      "cost_info": {
        "read_cost": "0.25",
        "eval_cost": "0.10",
        "prefix_cost": "0.35",
        "data_read_per_join": "1K"
      },
      "used_columns": [
        "id",
        "category",
        "title",
        "body"
      ],
      "attached_condition": "(`sample`.`samples`.`title` = 'hoge')"
    }
  }
} |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

PostgreSQLにも対応していて下記のような形で取得出来ます 🐘

$ require 'active_record_json_explain'  # モンキーパッチの有効化
=> true
Sample.with_title.explain(json: true)
=> EXPLAIN for: SELECT "samples".* FROM "samples" WHERE "samples"."title" = $1 [["title", "hoge"]]
                                                                                                                                                    QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
[
  {
    "Plan": {
      "Node Type": "Seq Scan",
      "Parallel Aware": false,
      "Relation Name": "samples",
      "Alias": "samples",
      "Startup Cost": 0.00,
      "Total Cost": 11.62,
      "Plan Rows": 1,
      "Plan Width": 556,
      "Filter": "((title)::text = 'hoge'::text)"
    }
  }
]
(1 row)

おわりに

前回の記事で書いた通りJSON形式のEXPLAINは特にMySQLだとオプティマイザが判断したクエリのコストが表示されリファクタリング時に有用な情報を取得出来るので便利かなと思います👍
クエリチューニングを行うとき等に使っていただければ🙌