最近 SQL にはまっているので、ついでに Rails というか ActiveRecord で発行された SQL query をカウントする gem を書いた。

最近 SQL にはまっているので、ついでに Rails というか ActiveRecord で発行された SQL query をカウントする gem を書いた。

Installation

gem 'a_r_q_logger'

github.com

せつめい

ActiveRecord::Base サブクラスのインスタンス生成をできるだけ抑えれば、それだけ処理時間が抑えられるのは自明とされています。

そこで、今は、できるだけ少ない queries に抑えることを目的として SQL の練習をしていますので、すぐわかるようにしました。

その練習を進めていく上で、その 1 query の処理時間が気になっており、SQL のみの処理時間もとれるようにしましたというか、出発点はこっちです。

pry(main)> ARQLogger.log { 3.times { TestModel.has_children(10).map(&:children_count) } }
=> #<struct ARQLogger::Result count=3, msec=1508.4>

from 句で前もって as しておくのと、CASEWHEN ごとに (複数の) 集約関数を書くのではどっちが速いのか気になって、こういうことをしていたのです。 (おそらくオプティマイザがいい感じにしてくれるおかげで、CASE に同じ式を何回も書くほうが速かった)

テストでも使える

query を抑えようと書いたかっこいい生 SQLが、善意の第三者により書きかえられるということはありがちなことですが、amount of queries を抑えようと書いた生 SQL が、その善意により too many queries になっては目もあてられません。

query 発行量はコントロールできる要素ですから、これはテストで縛っておくことができます。

たとえば以下は N + 1 の解決法でよく出てくる includes の付与テストです。

it do
  expect(ARQLogger.log {
    TestModel.includes(:test_child_models).all.each { |m| m.test_child_models.map(&:name) }
  }.count).to eq(2)
end
it do
  expect(ARQLogger.log {
    TestModel.all.each { |m| m.test_child_models.map(&:name) }
  }.count).to eq(11)
end

久しぶりに gem を書くのが目的だった

どこかにありそうだなと思って軽く調べて、そんな感じの gem もありましたが軽く無視って書きました。

たのしかたです。