Запрос разрешен в SQLite, поскольку он позволяет SELECT
элементам списка ссылаться на несгруппированные столбцы вне агрегатных функций или без того, чтобы указанные столбцы были функционально зависимы от выражений группировки. Неагрегированные значения выбираются из произвольной строки в группе.
Кроме того, в sidenote задокументировано, что специальная обработка «пустых» столбцов в агрегированном запросе происходит, когда агрегат равен min()
или max()
1 :
Когда агрегатные функции min()
или max()
используются в агрегированном запросе, все пустые столбцы в наборе результатов получают значения из входной строки, которая также содержит минимум или максимум.
Это относится только к простым запросам, и снова возникает неоднозначность, если более 1 строки имеют одинаковые min / max или запрос содержит более 1 вызова min()
/ max()
.
Это делает SQLite несоответствующим в этом отношении, по крайней мере, со стандартом SQL: 2003 (я уверен, что это не сильно изменилось в более новых версиях):
7.12 <спецификация запроса>
Функция
Укажите таблицу, полученную из результата <выражения таблицы>.
Формат
<query specification> ::=
SELECT [ <set quantifier> ] <select list> <table expression>
...
Правила соответствия
...
3) Без функции T301 «Функциональные зависимости» в соответствующем языке SQL, если T является сгруппированной таблицей, то в каждом <значении-выражении>, содержащемся в <списке выбора>, каждая <ссылка на столбец>, которая ссылается на столбец T должен ссылаться на столбец группировки или указываться в агрегированном аргументе <спецификации функции набора>.
Большинство других СУБД SQL, таких как Postgresql, более точно следуют стандарту в этом отношении и требуют, чтобы список SELECT
агрегированного запроса состоял только из выражений группировки, агрегатных выражений или что любые разгруппированные столбцы являются функционально зависимыми на сгруппированных столбцах.
В Postgresql требуется другой подход, чтобы получить такой результат наибольшее-на-группу . Есть много замечательных постов , которые освещают эту тему, но вот краткое изложение одного подхода, специфичного для Postgresql Используя расширение DISTINCT ON
в сочетании с ORDER BY
, вы можете достичь тех же результатов:
@classmethod
def find_recent_by_section_id_list(
cls, section_id_list: List) -> List["SectionStatusModel"]:
return (
cls.query
.filter(cls.section_id.in_(section_id_list))
.distinct(cls.section_id)
# Use _id as a tie breaker, in order to avoid non-determinism
.order_by(cls.section_id, cls.update_datetime.desc(), cls._id)
)
Естественно, это потом сломается в SQLite, так как он не поддерживает DISTINCT ON
. Если вам нужно решение, которое работает в обоих случаях, используйте подход оконной функции row_number()
.
1: обратите внимание, что это означает, что ваше предложение HAVING
на самом деле не сильно фильтруется, поскольку разгруппированное значение всегда будет выбираться из строки, содержащей максимальное значение. Это просто присутствие этого max(update_datetime)
, которое делает трюк.