🔝

ページネーションで重複データが表示される??

2024/07/10に公開

こんにちは!
ラブグラフエンジニアのひろです。

今回は、ページャーを搭載しているページで重複表示が発生したときの、原因と解決方法についてまとめます。

起きていたこと

ページネーションを搭載した、カメラマンを一覧表示するページで
ページ1の最後のカメラマンとページ2の最初のカメラマンが重複する問題が発生しました。

原因調査

Rails console を使って、 controller で使っているものと同じ where 文を実行し、データの重複を検証しました。
すると確かに同じデータが返ってきていました。

example.rb
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 カラムを使うことで解決しました。

example.rb
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