Я хочу запустить этот запрос:
SELECT DISTINCT ON (address_id) purchases.address_id, purchases.*
FROM purchases
WHERE purchases.product_id = 1
ORDER BY purchases.purchased_at DESC
Но я получаю эту ошибку:
PG:: Ошибка: ошибки 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 на группу, которая может быть решаются следующими подходами:
Общее решение, которое должно работать в большинстве СУБД:
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
Более ориентированное на PostgreSQL решение на основе ответа @hkf:
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
, я не думаю, что это рабочий код?
A подзапрос может решить эту проблему:
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
.
Связанный, с большим количеством объяснений:
DISTINCT ON
без соответствующего ORDER BY
. Первый запрос требует ORDER BY address_id
внутри подзапроса.
DISTINCT ON
без ORDER BY
в том же запросе. В этом случае вы получаете произвольную строку от каждого набора пиров, определенных предложением DISTINCT ON
. Попробуйте или перейдите по ссылкам выше для получения подробной информации и ссылок на руководство. ORDER BY
в том же запросе (тот же SELECT
) просто не может не согласиться с DISTINCT ON
. Я тоже это объяснил.
Функция окна может решить, что за один проход:
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)
address_id
. Принцип может работать, хотя. Связанные примеры: stackoverflow.com/a/22064571/939860 или stackoverflow.com/a/11533808/939860 . Но есть более короткие и / или более быстрые запросы для решения проблемы.
Для всех, кто использует 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))
query.distinct(foo).from_self().order(bar)
Purchases.query
?
Вы также можете сделать это, используя предложение 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
purchases
не имеет только две колонки address_id
и purchased_at
). Из-за GROUP BY
вам нужно будет использовать статистическую функцию, чтобы получить значение каждого столбца, не используемого для группировки, поэтому все значения будут поступать из разных строк группы, если вы не пройдете некрасивую и неэффективную гимнастику. Это можно исправить только с помощью оконных функций, а не GROUP BY
.