このクエリを実行したい:
SELECT DISTINCT ON (address_id) purchases.address_id, purchases.*
FROM purchases
WHERE purchases.product_id = 1
ORDER BY purchases.purchased_at DESC
しかし、私はこのエラーを受け取ります:
PG ::エラー:エラー:SELECT DISTINCT ON式は最初のORDER BY式と一致する必要があります
address_id
を最初のORDER BY
式として追加すると、エラーは沈黙しますが、address_id
に並べ替えを追加したくありません。 address_id
で注文せずに行うことは可能ですか?
ドキュメントは言う:
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
ここで問題を明確にし、拡張し、解決しました: ある列で順序付けられ、別の列で区別される行を選択する
サブクエリで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
subqueryはそれを解決できます:
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
を検討してください。
関連、詳細説明:
ウィンドウ関数は、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)
Flask-SQLAlchemyを使用している人にとっては、これは私のために働いた
from app import db
from app.models import Purchases
from sqlalchemy.orm import aliased
from sqlalchemy import desc
stmt = Purchases.query.distinct(Purchases.address_id).subquery('purchases')
alias = aliased(Purchases, stmt)
distinct = db.session.query(alias)
distinct.order_by(desc(alias.purchased_at))
SELECT DISTINCT ON (address_id) purchases.address_id, purchases.*
FROM purchases
WHERE purchases.product_id = 1
ORDER BY address_id, purchases.purchased_at DESC
ORDER BY address_id、purchases.purchased_at DESC
dISTINCT ON()関数のために、address_idを順番に追加する必要があります