3

私は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

しかし、私はまだ投票の価値を得ており、2つと読みます。

4

2 に答える 2

6

func.count(Read.id).label('total')に置き換えるだけで、この特定の問題を解決できるように見えるかもしれませんfunc.count(func.distinct(Read.id)).label('total')。そして実際、これは読み取り数の問題を解決します。

しかし、あなたの投稿に対して突然別の読者を獲得した場合 (最終的に 2 人の読者と 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)
        )

注: クエリに a がありますが、クエリに句User.usernameがありませんでした。joinこれもチェックしてみるといいかもしれません。

于 2012-09-21T15:13:28.037 に答える
2

複数のテーブルを結合する場合、先に結合したテーブルの行が、後で結合するテーブルに対して 1 対多の関係で繰り返されます (簡単に言えば)。これが、カウントがオフになっている理由です。このような結合では、主キーなど、結果セットでカウントするために常に異なるものを見つける必要があります。これははるかに高速であるため、サブクエリよりも好ましいと思います。実際、私が行っているパフォーマンス チューニングの多くは、サブクエリを排除することから来ています。

したがって、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 または別のフィルターをそれに追加して、投稿ごとのカウントを取得することも必要になるでしょう。これはおそらく目標です。

于 2017-08-06T02:31:37.040 に答える