ページネーションで重複データが表示される??
こんにちは!
ラブグラフエンジニアのひろです。
今回は、ページャーを搭載しているページで重複表示が発生したときの、原因と解決方法についてまとめます。
起きていたこと
ページネーションを搭載した、カメラマンを一覧表示するページで
ページ1の最後のカメラマンとページ2の最初のカメラマンが重複する問題が発生しました。
原因調査
Rails console を使って、 controller で使っているものと同じ where 文を実行し、データの重複を検証しました。
すると確かに同じデータが返ってきていました。
limit = 48
page = 1
photographers =
Photographer
.order(rank: :desc)
.order(albums_count: :desc)
.page(page).per(limit)
# page=1 と page=2 で重複
photographers.last # page=1 のとき ID: 100 ひろ
photographers.first # page=2 のときも ID: 100 ひろ
不思議ですね、チームメンバーから情報をもらうまで原因に気付けませんでした。
原因
原因は MySQL の仕様にありました。
ユニークではないカラムで .order をおこなうと、データの順番が保証されないのです。
参考ブログ:SQLのORDER BYは完全ではない
MySQL バグレポート:LIMIT clause results in duplicate data across pages
カメラマンを取得する際におこなっていたソートは
- ランクの高い順
.order(rank: :desc) - 納品数順
.order(album_count: :desc)
のみで、これらは同じ値を持ったカメラマンが存在しうるものでした。
解決方法
この問題を解決するためには、ユニークなカラムで .order を行う必要があります。
今回のケースでは created_at カラムを使うことで解決しました。
limit = 48
page = 1
photographers =
Photographer
.order(rank: :desc)
.order(albums_count: :desc)
.order(:created_at)
.page(page).per(limit)
# page=1 と page=2 で重複しない
photographers.last # page=1 のとき ID: 100 ひろ
photographers.first # page=2 のとき ID: 200 くま
この他にも、 id カラムなどのユニークなカラムを使うことで重複を回避できます。
まとめ
今回の件で、ページネーションをおこなう際には、必ずユニークなカラムでソートしなければいけないことを学びました。
また、SQLの発行が変わらないように見えても、出力結果が異なることがあるということを再認識できました。
まだこのようなケースに遭遇したことがない方も、今後の開発に役立てていただければ幸いです。
Discussion