前提・実現したいこと
コメントをいいね順に取得しつつ、ページネーションを実装したいです。
発生している問題
しかしページネーション実行中にいいね数が更新された場合、ページネーションの取得内容にモレや重複が生じてしまいます。
例えば次の流れです。
該当のソースコード
まずコメントテーブルの CREATE
と、初期値の INSERT
をこうします。
like_count
がソート対象のカラムです。
SQL
1# コメントテーブル 2CREATE TABLE `comments` ( 3 `ID` bigint(20) NOT NULL AUTO_INCREMENT, 4 `like_count` bigint(20) NOT NULL DEFAULT 0, 5 `created_at` datetime NOT NULL, 6 `updated_at` datetime NOT NULL, 7 PRIMARY KEY (`ID`) 8); 9 10# 初期値 11INSERT INTO comments (`ID`,`like_count`,`created_at`,`updated_at`) 12VALUES 13(1,30,'2022-01-01 01:00:00','2022-01-01 01:00:00'), 14(2,30,'2022-01-02 01:00:00','2022-01-02 01:00:00'), 15(3,30,'2022-01-03 01:00:00','2022-01-03 01:00:00'), 16(4,50,'2022-01-04 01:00:00','2022-01-04 01:00:00'), 17(5,70,'2022-01-05 01:00:00','2022-01-05 01:00:00'), 18(6,20,'2022-01-06 01:00:00','2022-01-06 01:00:00');
ここから2件ずつ取得するとして下記の SELECT
を流すと3ページに分割されます。
SQL
1SELECT ID, like_count, updated_at 2FROM comments 3GROUP BY ID 4ORDER BY like_count DESC, ID DESC 5-- ページネーション指定 6# LIMIT 0, 2; # 1ページ目 →【ID=5,4 が取得される】 7# LIMIT 2, 2; # 2ページ目 →【ID=3,2 が取得される】 8# LIMIT 4, 2; # 3ページ目 →【ID=1,6 が取得される】
しかし、1ページ目の読み込み直後に下記の UPDATE
がされた場合、2ページ目で
【ID=3,2 が取得される】 でなく
【ID=2,1 が取得される】 になってしまい、ID=3 が取得からモレるという問題です。
SQL
1UPDATE `comments` 2SET like_count = 20, updated_at = '2022-02-01 01:00:01' 3WHERE ID = 4;
以上の問題はこちらで実行して頂けますのでよろしければどうぞ。
http://sqlfiddle.com/#!9/3d1ac7/1
試したこと
まず検索で次の記事を見つけましたが、いずれもソートカラムが可変でないケースに限るように見受けられます。
■Pagination(ページネーション)がずれる、抜ける問題を解決する
https://qiita.com/marnie_ms4/items/1dc51042844c85c30550
■次ページの取得
https://use-the-index-luke.com/ja/sql/partial-results/fetch-next-page
続いて OFFSET
を用いず次のように WHERE
でのアプローチを試みました。?
には直前ページの最後のコンテンツのlike_count
を入れ、ページ訪問日時をupdate_at
に入れます。これにより重複は防げますが、新たなモレが生じてしまい実現できませんでした。
SQL
1WHERE 2( like_count = ? AND ID < ? ) OR 3( like_count < ? AND updated_at < ? )
補足情報(FW/ツールのバージョンなど)
MySQL は 5.7 を使用しております。
宜しくお願い申し上げます。