python - 如何使用 GROUP BY 和 HAVING 与 SQLAlchemy 和 Postgresql 获取具有最大更新日期时间的行

标签 python postgresql sqlite group-by sqlalchemy

我要从 SQLite 转到 Postgresql。这使我的一个查询不起作用。我不清楚为什么在 SQLite 中允许此查询,但在 Postgresql 中不允许。有问题的查询在下面的 find_recent_by_section_id_list() 函数中。

我尝试过以多种方式重写查询,但令我困惑的是,当我使用 SQLite 时,这个查询仍然有效。

设置是 Flask、SQLAlchemy、Flask-SQLAlchemy 和 Postgresql。

class SectionStatusModel(db.Model):

    __tablename__ = "sectionstatus"
    _id = db.Column(db.Integer, primary_key=True)
    update_datetime = db.Column(db.DateTime, nullable=False)
    status = db.Column(db.Integer, nullable=False, default=0)
    section_id = db.Column(db.Integer, db.ForeignKey("sections._id"), nullable=False)

    __table_args__ = (
        UniqueConstraint("section_id", "update_datetime", name="section_time"),
    )


    @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))
            .group_by(cls.section_id)
            .having(func.max(cls.update_datetime) == cls.update_datetime)
        )

我希望此查询会返回每个部分的最新部分状态,但是我收到以下错误:

E       sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) column "sectionstatus._id" must appear in the GROUP BY clause or be used in an aggregate function
E       LINE 1: SELECT sectionstatus._id AS sectionstatus__id, sectionstatus...
E                      ^
E       
E       [SQL: SELECT sectionstatus._id AS sectionstatus__id, sectionstatus.update_datetime AS sectionstatus_update_datetime, sectionstatus.status AS sectionstatus_status, sectionstatus.section_id AS sectionstatus_section_id 
E       FROM sectionstatus 
E       WHERE sectionstatus.section_id IN (%(section_id_1)s, %(section_id_2)s) GROUP BY sectionstatus.section_id 
E       HAVING max(sectionstatus.update_datetime) = sectionstatus.update_datetime]
E       [parameters: {'section_id_1': 1, 'section_id_2': 2}]
E       (Background on this error at: http://sqlalche.me/e/f405)

这是测试套件的输出。

最佳答案

SQLite 中允许查询,因为它 allows SELECT list items to refer to ungrouped columns在聚合函数之外,或者所述列在功能上不依赖于分组表达式。非聚合值是从组中的任意行中选取的。

此外,它记录在 sidenote 中当聚合为 min()max() 1 时,对聚合查询中的“裸”列进行特殊处理:

When the min() or max() aggregate functions are used in an aggregate query, all bare columns in the result set take values from the input row which also contains the minimum or maximum.

这仅适用于简单查询,如果超过 1 行具有相同的最小值/最大值,或者查询包含超过 1 次对 min()/ 的调用,则再次存在歧义最大()

这使得 SQLite 在这方面不符合要求,至少对于 SQL:2003 标准而言(我很确定这在新版本中没有太大变化):

7.12 <query specification>

Function

Specify a table derived from the result of a <table expression>.

Format

<query specification> ::=
    SELECT [ <set quantifier> ] <select list> <table expression>

...

Conformance Rules

...

3) Without Feature T301, “Functional dependencies”, in conforming SQL language, if T is a grouped table, then in each <value expression> contained in the <select list>, each <column reference> that references a column of T shall reference a grouping column or be specified in an aggregated argument of a <set function specification>.

大多数其他 SQL DBMS,例如 Postgresql,在这方面更严格地遵循标准,并要求聚合查询的 SELECT 列表仅包含分组表达式、聚合表达式,或者任何未分组的列在功能上依赖于分组的列。

在 Postgresql 中需要一种不同的方法来获取这种 结果。有很多great posts涵盖了这个主题,但这里是一个 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) 就可以了。

关于python - 如何使用 GROUP BY 和 HAVING 与 SQLAlchemy 和 Postgresql 获取具有最大更新日期时间的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55419442/

相关文章:

python - 传递列表并将其长度设置为python中的默认参数

python - 获取 chown() : Operation not permitted while running uwsgi from ini file

postgresql - Golang postgres 提交未知命令错误?

java - 将 Intent 转换为字符串,反之亦然

database - 分配给 SQLite 内存数据库的内存大小

python - 使用keras生成器时什么是纪元?

python - 创建自定义类QPointF

ruby-on-rails - 数据库迁移脚本中断引用

node.js - 无法连接到 EC2 上的 Postgres : ECONNREFUSED

sql: sqlite: 服务器端分页