p.tatapa.org

p.tatapa.org

https://soudai.hatenablog.com/entry/2024/12/10/115848

履歴テーブルから各ユーザの最新のデータを1件ずつ取ってきたい場合、PostgreSQLの場合は次のように書いた方が高速な場合がある。

SELECT
  latest.*
FROM
  users,
  LATERAL (
    SELECT
      *
    FROM
      history
    WHERE
      history.user_id = users.user_id
    ORDER BY
      created_at DESC
    LIMIT
      1
  ) AS latest;

ウィンドウ関数を使う場合やDISTINCT ONを使う場合、インデックスがあっても線形スキャンとなる。

一方、前記のクエリの場合(あるいは入れ子ループでインデックスを引くような同等のクエリの場合)、インデックスを使って最新の行のみにアクセスするため、最新以外の行が多い場合は高速になる。

さらに複雑な条件が必要な場合は再帰CTEを使った方法が使える: https://wiki.postgresql.org/wiki/Loose_indexscan

多くの場合は最新データを別テーブルにする方法でよいんだけど、任意の時点における最新データを取得したい場合や、何らかの理由で別テーブルを作りたくない場合はこの手法が使える。

インデックスは(user_id DESC, created_at DESC)で作っておくとよい。

いずれにしろ実行計画を見て、さらに実データに近いデータでベンチマークを取ってちゃんと速くなっているか確認する必要がある。

replies
0
announces
0
likes
0