从至少有2条评论的课程开始,选择评论最多的课程

Joe*_* JJ 3 postgresql sqlalchemy flask-sqlalchemy

我正在使用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') )
Run Code Online (Sandbox Code Playgroud)

我想从至少两篇评论中选择最受评论的课程.以下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()
Run Code Online (Sandbox Code Playgroud)

但是当我在生产中切换到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}
Run Code Online (Sandbox Code Playgroud)

我尝试通过在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()
Run Code Online (Sandbox Code Playgroud)

任何人都可以帮我解决这个问题.非常感谢

zzz*_*eek 5

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

因此,您对Review的查询基本上会返回给您每个不同课程ID的第一个"Review"行 - 例如,对于课程ID 3,如果您有七个"Review"行,则只需在组中选择一个基本上随机的"Review"行. "COURSE_ID = 3".我收集你真正想要的答案,"课程",可以在这里找到,因为你可以选择半随机选择的Review对象并在其上调用".course",为你提供正确的课程,但这是一种向后的方式.

但是一旦你使用像Postgresql这样的正确数据库,你需要使用正确的SQL.您需要从"审核"表中获取的数据只是course_id和count,没有别的,所以只查询(首先假设我们实际上不需要显示计数,这是一分钟):

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()
Run Code Online (Sandbox Code Playgroud)

但这不是您的课程对象 - 您想要获取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()
Run Code Online (Sandbox Code Playgroud)

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

 courses = session.query(Course).filter(
       Course.id.in_(most_rated_course_id_subquery)).all()
Run Code Online (Sandbox Code Playgroud)

但这基本上会抛弃你正在寻找的"ORDER BY",并且也没有给我们任何实际报告这些计数以及课程结果的好方法.我们需要将这些数据与我们的课程一起计算,以便我们可以报告并按顺序进行排序.为此,我们使用"课程"表中的JOIN到我们的派生表.如果我们只是调用,那么SQLAlchemy足够聪明,可以知道加入"course_id"外键join():

courses = session.query(Course).join(most_rated_course_id_subquery).all()
Run Code Online (Sandbox Code Playgroud)

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

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()
Run Code Online (Sandbox Code Playgroud)

一篇很棒的文章我想向人们指出GROUP BY和这种查询是SQL GROUP BY技术,它指出了"从A连接选择到(带有聚合/ GROUP BY的B的子查询)"模式的共同需要.