PostgreSQLではINTEGER型のカラムをorderでソートするときNULLが先頭に来てしまう
またしてもデータベースの違いでつまづきました。
とりあえずこれですべて片付いたとは思うけど、今度から開発環境と本番環境で同じDBを使おうと心に誓いました……。
現象
記事(article)にいいね数(likes_count)カラムがあり、その数値でソートして人気記事ランキングを作ります。
article.rb
#like数が多い記事TOP10 def self.popular_10 includes(:blog).order(:likes_count).reverse[0..9] end
開発環境のSQLiteではきちんといいね数が多い順に取得できていたのですが、
HerokuにデプロイしてPostgreSQLになったらなぜかいいね数ゼロの記事を取得してしまう。
原因
PostgreSQLではinteger型のカラムをorderでソートしたとき、デフォルトでnull値が先頭に来てしまうようです。
いいね数がnullのデータが3つと 1, 10, 30のデータがあるとして、これをDESCで並び替えると
null
null
null
30
10
1
というように並びます。
今回の場合はいいね数0の記事が10個以上あるので、nullの記事に邪魔されて取得できなかったのですね。
解決法
#like数が多い10記事からランダムに取得 def self.popular_10 includes(:blog).order('likes_count IS NULL, likes_count DESC')[0..9] end
このようにすればNULLの値が最後になり、意図した通りの動きをします。
IS NOT NULL
にするとNULLの値が最初になります。何も記述しない場合と同じ動きですね。
ちなみにPostgreSQLだと
order('likes_count DESC NULLS LAST')
という記述も可能だそうです。こっちのほうがスマートですね。
ただSQLiteでは使えませんので注意。