CGMなどを作っていると、現在人気の投稿をトレンドとしてランキング表示したいときがある
これをSQLだけで実装する
パフォーマンスも何も考えていないしテーブル構成にも依存しているし、さらには手動で調整するマジックナンバーさえある
だが、簡単なのでとりあえずそれっぽい機能を付けたいとなったときに役立つかもしれない
PostgreSQL
ランク付けの対象として投稿を表すPOSTテーブルがあり、閲覧数を表すVIEWテーブルと一対多のリレーションになっているとする(閲覧されるたびにVIEWテーブルのレコードが増えるパターン)
また、各テーブルには作成日時created_atと更新日時updated_atがあるとする
SELECT "post".*, (COUNT(DISTINCT view)) as rank_point
FROM "post"
LEFT OUTER JOIN "view" ON "view"."post_id" = "post"."id" AND "view"."created_at" >= now() - interval '1 hour'
GROUP BY post.id
ORDER BY rank_point DESC;
LEFT OUTER JOINで結合する時にVIEWテーブルの条件を調整している
"view"."created_at" >= now() - interval '1 hour'
は現在時刻から1時間前までの間に作成されたVIEWのレコードのみ結合するという条件で、これをカウントしてrank_pointとし、post.idでグルーピングしたものを降順に並べれば1時間のうちで閲覧が多い投稿が順に並ぶはずである
interval '1 hour'
の部分は好きなように調整できる
他の指標を使いたくなったらいくらでも追加できる
例えば投稿には一対多でコメントがあって、コメントの多さでも人気なことを表したいとする
SELECT "post".*, ( (COUNT(DISTINCT view) * 10) + (COUNT(DISTINCT comment) * 1000 ) ) / 2 as rank_point
FROM "post"
LEFT OUTER JOIN "view" ON "view"."post_id" = "post"."id" AND "view"."created_at" >= now() - interval '1 hour'
LEFT OUTER JOIN "comment" ON "comment"."post_id" = "post"."id" AND "comment"."created_at" >= now() - interval '1 hour'
GROUP BY post.id
ORDER BY rank_point DESC;
VIEWに加えてCOMMENTも同じように結合する
あとは適当に掛け算して桁を合わせてから指標の数で割ったものをrank_pointとするだけだ
桁を合わせるとは書いたが、重視したい指標に多めの数値を掛けるなりすれば重み付けもできる
最初はちゃんと移動平均を出そうと思ってWindow関数とか調べたが、複数テーブルが絡むと面倒な記述になりそうでやめてしまった
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント