な… 何を言っているのかわからねーと思うが
おれも 何をされたのか わからなかった…
頭がどうにかなりそうだった…
reverse_orderをチェーンしていただとか超スピードだとか
そんなチャチなもんじゃあ断じてねえ
もっと恐ろしいものの片鱗を味わったぜ…
ということで、前置きはここまでにして実際に私が体験した話です。
タイトルの通りorder(created_at: :asc)
と実装したのに、SQLはcreated_at desc
と実行される実装について説明します。
ここまでの話だけで、パッと実装が思い浮かぶ方はActiveRecordマスターだと思います…!
私は10年弱Rails触っていますが、初めて気づきましたw
実装
わかってしまえば、やり方は簡単です。
Userというモデルがある場合、下記実装では発行されるSQLのorderは逆になります。
User.order(created_at: :asc).last
> User.order(created_at: :asc).last
User Load (0.6ms) SELECT `users`.* FROM `users` ORDER BY `users`.`created_at` DESC LIMIT 1
実装とSQLでorderが逆になっていますね。
これに初めて気づいたときはとても驚いたのですが、SQLファーストで考えると単純な話で、SQLには最後の値のみを取得する命令がありません。
一方、先頭の値であれば、limit 1
で取得できます。
そのためSQLで最後尾の値を取得したい場合はorderを逆にして先頭の値のみを取得することでやりたいことが実現できます。
ActiveRecordは実装した通りのSQLを発行するのではなく、全てのチェーンを考慮して効率の良いSQLに変換してくれているのですね。
SQLでは全件取得してRubyの処理で最後の1件だけ返すみたいな非効率なことはしていないようです。
改めてActiveRecordすげーとなりました!
おまけ1
SQLでは全件取得してRubyの処理で最後の1件だけ返すみたいな非効率なことはしていないようです。
と書きましたが、最初からこの実装だったのか気になったのでRailsのプルリクエストを探ってみました。
該当のプルリクエストはこちらだと思います。
2016年1月にマージされているので、Rails 5.0.0から、今回紹介した挙動になったと思われます。
それ以前はlast
を使うとSQLでは全件読み込み、Rubyの処理で最後の1件を返却する実装だったようです。
おまけ2
ActiveRecordでlastを使った場合、指定したorderを逆にするということがわかりました。
全件をメモリにロードするわけではないので非効率ではなさそうですが、最初からorderを逆にしてfirstにしておく方が実装と発行されるSQLが揃うので可読性が上がる気がします。
ActiveRecordでlastを使っている実装を見つけた場合
[FYI]
order逆にしてfirstにした方が実装とSQLが揃って可読性が上がります。
lastをした場合、下記のようにRailsが最適化してorderを逆にするため、実装のorderと発行されるSQLのorderが逆になるためです
> User.order(created_at: :asc).last
User Load (0.6ms) SELECT `users`.* FROM `users` ORDER BY `users`.`created_at` DESC LIMIT 1
と指摘できると一目置かれるレビュアーになれるかもしれません!?