277

このクエリを実行したい:

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

しかし、私はこのエラーを受け取ります:

PG ::エラー:エラー:SELECTDISTINCTON式は最初のORDERBY式と一致する必要があります

address_id最初の式として追加するとORDER BYエラーが発生しなくなりますが、並べ替えを追加したくありませんaddress_id。注文せずに行うことはできaddress_idますか?

4

7 に答える 7

271

ドキュメントには次のように記載されています。

DISTINCT ON ( expression [, ...] ) は、指定された式が等しいと評価される行の各セットの最初の行のみを保持します。[...] ORDER BY を使用して目的の行が最初に表示されるようにしない限り、各セットの「最初の行」は予測できないことに注意してください。[...] DISTINCT ON 式は、一番左の ORDER BY 式と一致する必要があります。

公式文書

address_idそのため、を order byに追加する必要があります。

または、それぞれの最新の購入製品を含む完全な行を探しており、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

ここで問題が明確化され、拡張され、解決されました。ある列で並べられ、別の列で異なる行を選択する

于 2012-03-20T22:08:32.430 に答える
69

サブクエリで address_id で並べ替えてから、外側のクエリで必要なもので並べ替えることができます。

SELECT * FROM 
    (SELECT DISTINCT ON (address_id) purchases.address_id, purchases.* 
    FROM "purchases" 
    WHERE "purchases"."product_id" = 1 ORDER BY address_id DESC ) 
ORDER BY purchased_at DESC
于 2012-03-20T22:04:33.793 に答える
69

サブクエリはそれを解決できます:

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

の先頭の式は のORDER BY列と一致するDISTINCT ON必要があるため、同じ の異なる列で並べ替えることはできませんSELECT

ORDER BY各セットから特定の行を選択する場合にのみ、サブクエリで追加を使用します。

SELECT *
FROM  (
    SELECT DISTINCT ON (address_id) *
    FROM   purchases
    WHERE  product_id = 1
    ORDER  BY address_id, purchased_at DESC  -- get "latest" row per address_id
    ) p
ORDER  BY purchased_at DESC;

purchased_atできる場合はNULL、 - を使用DESC NULLS LASTしてインデックスを一致させ、最高のパフォーマンスを実現します。見る:

関連し、より多くの説明があります:

于 2012-03-20T22:40:26.973 に答える
11

ウィンドウ関数は、1回のパスでそれを解決できます:

SELECT DISTINCT ON (address_id) 
   LAST_VALUE(purchases.address_id) OVER wnd AS address_id
FROM "purchases"
WHERE "purchases"."product_id" = 1
WINDOW wnd AS (
   PARTITION BY address_id ORDER BY purchases.purchased_at DESC
   ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
于 2013-07-18T06:38:46.857 に答える
0

次のクエリと他の回答を使用して解決することもできます。

WITH purchase_data AS (
        SELECT address_id, purchased_at, product_id,
                row_number() OVER (PARTITION BY address_id ORDER BY purchased_at DESC) AS row_number
        FROM purchases
        WHERE product_id = 1)
SELECT address_id, purchased_at, product_id
FROM purchase_data where row_number = 1
于 2020-11-27T10:15:49.643 に答える