自分用のメモです。
TiDB環境で顕在化した。
tablehoges.present? などの事前ロードを済ませる系の処理以降で、同 tablehoges オブジェクトに対して tablehoges.last といった整列ありきで1件取得した場合に予期せぬデータが取得された。
原因は present? による事前ロードと present? でロードした同 ActiveRecord_Relation のオブジェクトに対して last を使用したこと。
アンチパターン
if tablehoges.present? # ← ORDER BY なしで全件ロードが走る
latest = tablehoges.last # ← ↑で事前ロードしたメモリ上の不定順序の配列の末尾を使ってしまう
end
ベストプラクティス
例1
わかりやすい。
ただし、当該処理がN+1が発生しうる処理かどうかの検討は必要。
if tablehoges.exists? # ← EXISTS クエリのみ。レコード自体はロードしない
latest = tablehoges.order(:id).last # ← 明示的にソートしてから取得
end
例2
クエリも1回なのでスマート。
last_record = tablehoges.order(:id).last # ← ORDER BY を明示
if last_record
latest = last_record
end
終わりに
当然ですが、order 句がなければ順番が保証されないデータとして取得されます。
自分が驚いたのは present? が事前ロードしているという点です。
ActiveRecord_Relation で last ってつけたら order(:id) という解釈になりクエリが発行されると思っていました。
気をつけます。