簡體   English   中英

PostgreSQL 具有不同 ORDER BY 的 DISTINCT ON

[英]PostgreSQL DISTINCT ON with different ORDER BY

我想運行這個查詢:

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 ( 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

子查詢可以解決它:

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 - 並匹配您的索引以獲得最佳性能。 看:

相關,有更多解釋:

窗口函數可以一次性解決這個問題:

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

也可以使用以下查詢和其他答案來解決。

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

試試這個查詢。

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

您也可以使用 group by 子句來完成此操作

   SELECT purchases.address_id, purchases.* FROM "purchases"
    WHERE "purchases"."product_id" = 1 GROUP BY address_id,
purchases.purchased_at ORDER purchases.purchased_at DESC

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM