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)
任何人都可以帮我解决这个问题.非常感谢
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的子查询)"模式的共同需要.