【问题标题】:Sqlalchemy using func with outerjoin on multiple tablesSqlalchemy 在多个表上使用带有外连接的 func
【发布时间】:2012-09-11 04:29:40
【问题描述】:

我在 sqlalchemy 中有以下表格:-

class Post(Base):
    __tablename__ = 'posts'
    id = Column(Integer, primary_key=True)
    compare_url =Column(String(200))
    url = Column(String(200))
    postedby = Column(Integer)
    category = Column(String(50))
    title  = Column(String(500),nullable=False)
    author  = Column(String(500),default="Unspecified")
    content = Column(Text(),default="could not fetch this content you will have to read it externally")
    summary = Column(Text())
    time = Column(TIMESTAMP(),default=now())
    post_type=Column(Text())
    Reads = relationship("Read", backref="Post")
    Reposts = relationship("RePost", backref="Post")
    Votes = relationship("Vote", backref="Post")



class Read(Base):
    __tablename__ = 'reads'
    id = Column(Integer, primary_key=True)
    post_read = Column(Integer, ForeignKey('posts.id'))
    #post = relationship("Post", backref=backref('Reads', order_by=id))
    time = Column(TIMESTAMP(),default=now())
    user_id = Column(String(50))


class Vote(Base):
    __tablename__ = 'votes'
    id = Column(Integer, primary_key=True)
    post_read = Column(Integer, ForeignKey('posts.id'))
    time = Column(TIMESTAMP(),default=now())
    user_id = Column(String(50))
    user_vote = Column(Boolean(),nullable=False)

我有这个问题

posts = session.query(Post, func.count(Read.id).label('total'),func.sum(Vote.user_vote).label('votes'),User.username).outerjoin(Post.Reads).outerjoin(Post.Votes)

我正在尝试获取投票数和帖子被阅读的次数。投票值可以是 -1 或 1

问题是我在每个帖子上的阅读次数和投票数都相同

例如当我的读取表有

id  post_read   time             user_id
1   7       2012-09-19 09:32:06  1

并且投票表有

id  post_read   time                 user_id    user_vote
1   7 [->]         2012-09-19 09:42:27  1   1
2   7 [->]         2012-09-19 09:42:27  2   1

但我仍然得到投票的价值,并且读为两个。

【问题讨论】:

    标签: python sql sqlalchemy pyramid


    【解决方案1】:

    看起来您可以通过简单地将func.count(Read.id).label('total') 替换为func.count(func.distinct(Read.id)).label('total') 来解决这个特定问题。事实上,这将解决读取次数的问题。

    但是,如果您突然为您的帖子增加了另一个读者(最终有 2 个读者和 2 个选民),那么您的所有选票也将被计算两次。

    最好的解决方案就是不要在同一个查询中聚合不同的项目。您可以使用子查询来解决这个问题:

    subq_read = (session.query(
                    Post.id, 
                    func.count(Read.id).label("total_read")
                ).
                outerjoin(Post.Reads).
                group_by(Read.post_read)
                ).subquery()
    
    subq_vote = (session.query(
                    Post.id, 
                    func.sum(Vote.user_vote).label("total_votes")
                ).
                outerjoin(Post.Votes).
                group_by(Vote.post_read)
                ).subquery()
    
    posts = (session.query(
                Post, 
                subq_read.c.total_read,
                subq_vote.c.total_votes,
            ).
            outerjoin(subq_read, subq_read.c.id == Post.id).
            outerjoin(subq_vote, subq_vote.c.id == Post.id)
            .group_by(Post)
            )
    

    注意:您的查询中有一个User.username,但我在查询中没有看到任何join 子句。您可能也想检查一下。

    【讨论】:

    • 我决定采用子查询样式,它可以完美运行。这正是我所需要的。
    【解决方案2】:

    当连接多个表时,较早连接的表会为稍后以一对多关系连接的表重复其行(简单地说)。这就是为什么你的计数是关闭的。在这样的连接中,您总是需要在结果集中找到一些不同的东西来计算……例如主键。我发现这比子查询更可取,因为它要快得多。事实上,我所做的大部分性能调整都来自于消除子查询。

    因此,如果您过滤 user_vote 列以消除您不想计算的记录,您可以像这样修复您的查询:

    posts = session.query(Post
        ,   func.count(distinct(Read.id)).label('total')
        ,   func.count(distinct(Vote.id)).label('votes')
        ,   User.username
        ) \
        .outerjoin(Post.Reads) \
        .outerjoin(Post.Votes) \
        .filter(Votes.user_vote == True)
    

    但是,您可能还想添加一个 group_by 或其他过滤器,以获取每个帖子的计数,这是您可能的目标。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-05
      • 2015-03-10
      • 2019-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多