DISTINCT ON PostgreSQL では、通常、これが最も簡単で最速です。 。
(特定のワークロードのパフォーマンスの最適化については、以下を参照してください。)
SELECT DISTINCT ON (customer)
id, customer, total
FROM purchases
ORDER BY customer, total DESC, id; または、出力列の序数で(明確でない場合は)短くします:
SELECT DISTINCT ON (2)
id, customer, total
FROM purchases
ORDER BY 2, 3 DESC, 1;
totalの場合 NULLにすることができます(どちらの方法でも問題はありませんが、既存のインデックスと一致させる必要があります):
...
ORDER BY customer, total DESC NULLS LAST, id; 主なポイント
DISTINCT ON は標準のPostgreSQL拡張機能です(DISTINCTのみ) 全体としてSELECT リストが定義されています。
DISTINCT ONに任意の数の式をリストします 句では、結合された行の値が重複を定義します。マニュアル:
明らかに、少なくとも1つの列の値が異なる場合、2つの行は別個のものと見なされます。 この比較では、ヌル値は等しいと見なされます。
大胆な強調鉱山。
DISTINCT ON ORDER BYと組み合わせることができます 。 ORDER BYの先頭の式 DISTINCT ONの式のセットに含まれている必要があります 、ただし、それらの間で自由に順序を並べ替えることができます。例。
追加追加できます ORDER BYへの式 ピアの各グループから特定の行を選択します。または、マニュアルに記載されているように:
DISTINCT ON式は、左端のORDER BYと一致する必要があります 式。ORDER BY句には通常、各DISTINCT ON内の行の望ましい優先順位を決定する追加の式が含まれます グループ。
idを追加しました 結びつきを断ち切る最後の項目として:
"idが最小の行を選択してください 最高のtotalを共有する各グループから 。"
グループごとの最初の並べ替え順序と一致しない方法で結果を並べ替えるには、別のORDER BYを使用して外部クエリのクエリの上にネストします。 。例。
totalの場合 NULLにすることもできますが、おそらく null以外の値が最大の行が必要です。 NULLS LASTを追加します 示されているように。参照:
- 列ASCで並べ替えますが、最初にNULL値を使用しますか?
SELECT リスト DISTINCT ONの式による制約を受けません またはORDER BY とにかく。 (上記の単純なケースでは必要ありません):
-
する必要はありません
DISTINCT ONに任意の式を含めます またはORDER BY。 -
できます
SELECTに他の式を含める リスト。これは、はるかに複雑なクエリをサブクエリや集計/ウィンドウ関数に置き換えるのに役立ちます。
私はPostgresバージョン8.3– 13でテストしましたが、この機能は少なくともバージョン7.1から存在しているので、基本的には常にそうです。
インデックス
完璧 上記のクエリのインデックスは、一致する順序で、一致する並べ替え順序で3つの列すべてにまたがる複数列のインデックスになります。
CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);
専門的すぎるかもしれません。ただし、特定のクエリの読み取りパフォーマンスが重要な場合に使用してください。 DESC NULLS LASTがある場合 クエリでは、同じものをインデックスで使用して、並べ替え順序が一致し、インデックスが適用されるようにします。
有効性/パフォーマンスの最適化
クエリごとに調整されたインデックスを作成する前に、コストとメリットを比較検討してください。上記のインデックスの可能性は、データ分散に大きく依存します 。
インデックスは、事前にソートされたデータを提供するために使用されます。 Postgres 9.2以降では、クエリはインデックスのみのスキャンの恩恵を受けることもできます インデックスが基になるテーブルよりも小さい場合。ただし、インデックス全体をスキャンする必要があります。
少数の場合 顧客あたりの行数 (列customerのカーディナリティが高い )、これは非常に効率的です。とにかくソートされた出力が必要な場合はさらにそうです。顧客あたりの行数が増えると、メリットは減少します。
理想的には、十分な work_memがあります。 関係するソートステップをRAMで処理し、ディスクに流出しないようにします。ただし、通常はwork_memを設定します あまりにも 高は悪影響を与える可能性があります。 SET LOCALを検討してください 非常に大きなクエリの場合。 EXPLAIN ANALYZEで必要な金額を見つけてください 。 「ディスク:」の言及 「並べ替え手順で、さらに必要なことを示します:
- Linux上のPostgreSQLの構成パラメーターwork_mem
- ORDERBYの日付とテキストを使用して単純なクエリを最適化する
多くの場合 顧客あたりの行数 (列customerのカーディナリティが低い )、ルーズインデックススキャン (別名「スキップスキャン」)は(はるかに)効率的ですが、Postgres 14までは実装されていません(インデックスのみのスキャンの実装はPostgres 15用に開発中です。こことここを参照してください。)
For現在、より高速なクエリ手法があります これの代わりに。特に、固有の顧客を保持する別のテーブルがある場合は、これが一般的なユースケースです。ただし、そうでない場合も:
- SELECT DISTINCTは、PostgreSQLのテーブルで予想よりも遅いです
- GROUP BYクエリを最適化して、ユーザーごとに最新の行を取得します
- グループごとの最大クエリを最適化する
- 行ごとに最後のN個の関連行をクエリする
ベンチマーク
別の回答を参照してください。