3

我将 Flask-SQLAlchemy 与 PostgreSQL 一起使用。我有以下两个模型:

class Course(db.Model):
    id = db.Column(db.Integer, primary_key = True )
    course_name =db.Column(db.String(120))
    course_description = db.Column(db.Text)
    course_reviews = db.relationship('Review', backref ='course', lazy ='dynamic')

class Review(db.Model):
    __table_args__ = ( db.UniqueConstraint('course_id', 'user_id'), { } )
    id = db.Column(db.Integer, primary_key = True )
    review_date = db.Column(db.DateTime)#default=db.func.now()
    review_comment = db.Column(db.Text)
    rating = db.Column(db.SmallInteger)
    course_id = db.Column(db.Integer, db.ForeignKey('course.id') )
    user_id = db.Column(db.Integer, db.ForeignKey('user.id') )

我想从至少两个评论开始选择评论最多的课程。以下 SQLAlchemy 查询在 SQlite 上运行良好:

most_rated_courses = db.session.query(models.Review, func.count(models.Review.course_id)).group_by(models.Review.course_id).\
          having(func.count(models.Review.course_id) >1) \   .order_by(func.count(models.Review.course_id).desc()).all()

但是当我在生产中切换到 PostgreSQL 时,它给了我以下错误:

ProgrammingError: (ProgrammingError) column "review.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT review.id AS review_id, review.review_date AS review_...
               ^
 'SELECT review.id AS review_id, review.review_date AS review_review_date, review.review_comment AS review_review_comment, review.rating AS review_rating, review.course_id AS review_course_id, review.user_id AS review_user_id, count(review.course_id) AS count_1 \nFROM review GROUP BY review.course_id \nHAVING count(review.course_id) > %(count_2)s ORDER BY count(review.course_id) DESC' {'count_2': 1}

我试图通过在 GROUP BY 子句中添加 models.Review 来修复查询,但它不起作用:

most_rated_courses = db.session.query(models.Review, func.count(models.Review.course_id)).group_by(models.Review.course_id).\
          having(func.count(models.Review.course_id) >1) \.order_by(func.count(models.Review.course_id).desc()).all()

谁能帮我解决这个问题。非常感谢

4

1 回答 1

5

SQLite 和 MySQL 都具有这样的行为,即它们允许具有聚合的查询(如 count())而不将 GROUP BY 应用于所有其他列 - 就标准 SQL 而言,这是无效的,因为如果聚合中存在多行组,它必须选择它看到的第一个返回,这基本上是随机的。

因此,您对 Review 的查询基本上会返回给您每个不同课程 ID 的第一个“Review”行 - 就像课程 ID 3 一样,如果您有七个“Review”行,它只是在组中选择一个基本上随机的“Review”行“课程 ID = 3”。我收集了您真正想要的答案,“课程”,可以在这里找到,因为您可以选择半随机选择的 Review 对象并在其上调用“.course”,从而为您提供正确的课程,但这是一种倒退的方式.

但是一旦你使用了像 Postgresql 这样的合适的数据库,你就需要使用正确的 SQL。您需要从“review”表中获取的数据只是 course_id 和计数,仅此而已,因此只需查询(首先假设我们实际上不需要显示计数,即在一分钟内):

most_rated_course_ids = session.query(
                        Review.course_id,
                    ).\
                    group_by(Review.course_id).\
                    having(func.count(Review.course_id) > 1).\
                    order_by(func.count(Review.course_id).desc()).\
                    all()

但这不是您的 Course 对象-您想获取该 ID 列表并将其应用于课程表。我们首先需要将课程 ID 列表保留为 SQL 结构,而不是加载数据——也就是说,通过将查询转换为子查询来将其转换为派生表(将单词 .all() 更改为 .subquery() ):

most_rated_course_id_subquery = session.query(
                    Review.course_id,
                ).\
                group_by(Review.course_id).\
                having(func.count(Review.course_id) > 1).\
                order_by(func.count(Review.course_id).desc()).\
                subquery()

将其链接到课程的一种简单方法是使用 IN:

 courses = session.query(Course).filter(
       Course.id.in_(most_rated_course_id_subquery)).all()

但这基本上会丢弃您正在寻找的“ORDER BY”,并且也没有给我们提供任何实际报告这些计数以及课程结果的好方法。我们需要将这个计数与我们的课程一起计算,以便我们可以报告它并按它排序。为此,我们使用从“课程”表到派生表的 JOIN。SQLAlchemy 足够聪明,可以知道加入“course_id”外键,如果我们只是调用join()

courses = session.query(Course).join(most_rated_course_id_subquery).all()

然后为了获得计数,我们需要将它与标签一起添加到子查询返回的列中,以便我们可以引用它:

most_rated_course_id_subquery = session.query(
                        Review.course_id,
                        func.count(Review.course_id).label("count")
                    ).\
                    group_by(Review.course_id).\
                    having(func.count(Review.course_id) > 1).\
                    subquery()

courses = session.query(
                Course, most_rated_course_id_subquery.c.count
            ).join(
                most_rated_course_id_subquery
            ).order_by(
                most_rated_course_id_subquery.c.count.desc()
            ).all()

我想向人们指出关于 GROUP BY 的一篇很棒的文章,这种查询是SQL GROUP BY 技术,它指出了“从 A 中选择连接到(B 的子查询与聚合/GROUP BY)”模式的共同需求。

于 2013-08-04T05:08:01.157 回答