1. ホーム
  2. sql

[解決済み】PostgreSQLのDISTINCT ONで異なるORDER BYを指定した場合

2022-04-02 05:37:02

質問

このクエリを実行したいのですが、どうすればいいですか?

SELECT DISTINCT ON (address_id) purchases.address_id, purchases.*
FROM purchases
WHERE purchases.product_id = 1
ORDER BY purchases.purchased_at DESC

でも、こんなエラーが出ます。

PG::Error: ERROR: SELECT DISTINCT ON 式は、最初の ORDER BY 式と一致しなければなりません。

追加 address_id を最初の ORDER BY 式はエラーを消しますが、私は本当に address_id . による順序付けなしで行うことは可能ですか? address_id ?

解決方法は?

ドキュメントによると

<ブロッククオート

DISTINCT ON ( 式 [, ...] ) は、与えられた式が等しいと評価される各行の集合の最初の行だけを保持します。[各セットの最初の行は、ORDER BYを使用して目的の行が最初に表示されるようにしない限り、予測不可能であることに注意してください。[DISTINCT ON式は、一番左のORDER BY式と一致しなければなりません。

公式ドキュメント

そのため address_id をオーダーバイに追加してください。

また、各製品の最新の購入商品を含む全行が必要な場合は address_id でソートした結果を purchased_at の場合、グループあたりの最大Nの問題を解こうとしていることになりますが、これは次のようなアプローチで解くことができます。

ほとんどのDBMSで動作するはずの一般的な解決策。

SELECT t1.* FROM purchases t1
JOIN (
    SELECT address_id, max(purchased_at) max_purchased_at
    FROM purchases
    WHERE product_id = 1
    GROUP BY address_id
) t2
ON t1.address_id = t2.address_id AND t1.purchased_at = t2.max_purchased_at
ORDER BY t1.purchased_at DESC

hkf さんの回答に基づいて、より PostgreSQL に近い解決策を提案します。

SELECT * FROM (
  SELECT DISTINCT ON (address_id) *
  FROM purchases 
  WHERE product_id = 1
  ORDER BY address_id, purchased_at DESC
) t
ORDER BY purchased_at DESC

問題の明確化、拡張、解決はここで。 ある列で並べられ、別の列で区別される行を選択する