[英]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.