【ITニュース解説】First/Last per Group: PostgreSQL DISTINCT ON and MongoDB DISTINCT_SCAN Performance
2025年10月04日に「Dev.to」が公開したITニュース「First/Last per Group: PostgreSQL DISTINCT ON and MongoDB DISTINCT_SCAN Performance」について初心者にもわかりやすく解説しています。
ITニュース概要
PostgreSQLで各グループの最初の行を効率良く取得するのは複雑だが、MongoDBは集約機能で$first/$lastと$sort/$groupを組み合わせることで、シンプルかつ高速に実現できる。これはDISTINCT_SCANという効率的なインデックススキャンによるものだ。
ITニュース解説
データベースを扱う際、「特定のグループの中から、最初に出てくるデータや最後に出てくるデータだけを選びたい」という場面はよくある。例えば、顧客ごとの最新の注文情報を取得したり、製品の最も古い在庫記録を探したりするケースがこれにあたる。一見するとシンプルな要求だが、データベースの種類や使い方によっては、これを効率的に実現するのが難しい課題となることがある。
リレーショナルデータベースの一つであるPostgreSQLでこの問題を解決しようとする場合、「DISTINCT ON ... ORDER BY」という構文が開発者にとっては最も直感的でわかりやすい方法だ。この構文は、指定したカラムの組み合わせで重複する行の中から、特定の順序で最初の1つだけを抽出する機能を提供する。しかし、この方法はパフォーマンスの面で大きな課題を抱えている。データベースは、最終的な結果を得るために、まず条件に一致する可能性のある全ての行を読み込み、その後で重複を排除して目的の行を絞り込むという処理を行う。もし対象となるデータ量が膨大であれば、この「全件読み込み」のプロセスに非常に時間がかかり、システムの負荷も高まってしまうのだ。具体的な実行計画を見ると、たとえ結果がわずか10件であっても、数十万件ものデータやバッファ(メモリ領域)を読み込んでいることが確認できる。
PostgreSQLで高いパフォーマンスを実現しようとすると、より複雑なクエリを記述する必要がある。例えば、「再帰的なCTE(Common Table Expression)」という、複数のクエリを組み合わせて段階的に結果を生成するような記述方法を用いることが考えられる。これは、必要なデータだけをインデックスを使って効率的に少しずつ読み進める「インデックスルーズスキャン」という高度な手法をシミュレートするものだ。インデックスルーズスキャンは、必要なデータに直接ジャンプし、不要なデータを読み飛ばすことで高速化を図るが、このような複雑なクエリは記述が難しく、データベースの内部動作を深く理解していないと正しく扱うのは困難である。
一方、ドキュメント指向データベースであるMongoDBでは、この「グループごとの最初(または最後)のデータを取得する」という問題を、よりシンプルかつ非常に効率的な方法で解決できる。MongoDBには「アグリゲーションパイプライン」という強力な機能があり、これは複数の処理ステージを連結することで、複雑なデータ変換や集計を段階的に実行できる仕組みだ。具体的には、まず「$match」ステージで条件に合うデータを絞り込み、次に「$sort」ステージでデータを特定の順序に並べ替える。そして「$group」ステージでデータをグループ化し、そのグループ内で「$first」や「$last」という演算子を使って最初または最後のデータを抽出する。最後に「$project」ステージで表示する項目を整形する、といった一連の操作をパイプラインとして記述するのだ。
このMongoDBのアグリゲーションパイプラインの優れた点は、その実行効率の高さにある。MongoDBは、パイプラインの構成、特に「$sort」と「$group」が「$first」または「$last」と組み合わせて使われていることを検知すると、条件に合った適切なインデックスが存在する場合に、「DISTINCT_SCAN」という特殊な最適化された実行ステージを自動的に利用する。DISTINCT_SCANは、インデックスを活用してデータを効率的に飛び飛びに読み進めることで、実際に結果に必要なドキュメントだけをピンポイントで取得する。これにより、PostgreSQLのDISTINCT ONのように大量のデータを一度に読み込むことなく、最小限のデータアクセスで結果を得ることが可能となる。例えば、PostgreSQLで数十万件のデータから10件の結果を得るのに膨大な読み込みが発生する状況でも、MongoDBはわずか10件のドキュメントと数件のインデックスエントリしか読み込まずに同等の結果を得られる。これは、必要な情報に直接ジャンプし、それ以外の不要なデータを読み飛ばす能力があることを意味する。
このような「グループごとの最初または最後のデータ」を取得する処理は、実際のビジネスアプリケーション開発で非常に頻繁に登場する。例えば、時系列データベースにおいて各測定項目ごとの最新データ、各サプライヤーとの最新の契約情報、各クライアントからの最後の購入履歴、各アカウントの最も新しい取引、各ユーザーの最初のログインイベント、各部署で最も給与の低い従業員など、多岐にわたる。PostgreSQLのようなリレーショナルデータベースでは、このような高パフォーマンスな解決策を実装するために専門的な知識と複雑なクエリ記述が求められることが多い。それに対してMongoDBでは、直感的でシンプルなアグリゲーションパイプラインによって、同等以上の効率性を容易に実現できるのだ。この違いは、開発のしやすさや、大規模なデータに対するスケーラビリティ(拡張性)の観点から、データベース選定において重要な要素となるだろう。