选择订阅特定用户但该用户未订阅的所有用户

时间:2012-08-08 19:03:04

标签: python sql sqlalchemy many-to-many flask-sqlalchemy

我有以下表格定义:

subscriptions = db.Table("subscriptions",
    db.Column("subscriber_id", db.Integer, db.ForeignKey("users.id"),
        primary_key=True),
    db.Column("subscribee_id", db.Integer, db.ForeignKey("users.id"), 
        primary_key=True),
)


class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)

    subscribes = db.relationship('User', secondary=subscriptions,
        backref=db.backref('subscribed'),
        primaryjoin="User.id==subscriptions.c.subscriber_id",
        secondaryjoin="User.id==subscriptions.c.subscribee_id",
    )

我需要形成一个查询,选择订阅特定用户但该用户未订阅的所有用户。我已经可以用Python做到这一点(他们可能会比我的描述更准确地描述我想要的内容):

filter(lambda u: u not in self.subscribes, self.subscribed)
# OR: set(self.subscribed) - set(self.subscribes)

但是如果在SQL端完成它会不会更快?

3 个答案:

答案 0 :(得分:3)

如果这是在SQL中完成的,我相信您的查询将类似于以下内容:

SELECT
    u.id
FROM
    subscriptions sub  JOIN
    users u ON u.id = sub.subscriber_id LEFT JOIN
    subscriptions unsub
        ON unsub.subscribee_id = sub.subscriber_id
        AND unsub.subscriber_id= sub.subscribee_id 
WHERE
    sub.subscribee_id = :user_id
    AND unsub.subscribee_id IS NULL

答案 1 :(得分:2)

这是SQLALchemy中的解决方案(它只是Michael Fredrickson对解决方案的翻译):

User1 = aliased(User)
sub = aliased(subscriptions)
unsub = aliased(subscriptions)


qry = db.session.query(User1).select_from(sub).\
    join(User1, User1.id==sub.c.subscriber_id).\
    filter(sub.c.subscribee_id==self.id).\
    outerjoin(unsub,
        and_(unsub.c.subscribee_id==sub.c.subscriber_id,
             unsub.c.subscriber_id==sub.c.subscribee_id)).\
    filter(unsub.c.subscribee_id==None).\
    order_by(User1.name.desc())

答案 2 :(得分:0)

如果您的数据库是Oracle,则可以使用“MINUS”关键字,该关键字将在集合中使用,就像最终评论中的减号一样。以下是单个语句,其结构类似于“UNION”查询:

SELECT subscriber_id FROM subscriptions WHERE subscribee_id = :myuid
MINUS
SELECT subscribee_id FROM subscriptions WHERE subscriber_id = :myuid

或者,如果“MINUS”不可用,您可以使用“NOT IN”;

SELECT subscriber_id
FROM subscriptions
WHERE subscribee_id = :myuid
  AND subscriber_id NOT IN
        (SELECT subscribee_id FROM subscriptions WHERE subscriber_id = :myuid)