web-dev-qa-db-ja.com

SQLAlchemy:少なくとも1つの多対多関連テーブルのメンバーシップでフィルタリング

SQLAlchemy0.7.1とMySQL5.1データベースを使用して、次のように多対多の関係を設定しました。

user_groups = Table('user_groups', Base.metadata,
    Column('user_id', String(128), ForeignKey('users.username')),
    Column('group_id', Integer, ForeignKey('groups.id'))
)

class ZKUser(Base, ZKTableAudit):
    __tablename__ = 'users'

    username   = Column(String(128), primary_key=True)
    first_name = Column(String(512))
    last_name  = Column(String(512))

    groups = relationship(ZKGroup, secondary=user_groups, backref='users')

class ZKGroup(Base, ZKTableAudit):
    __tablename__ = 'groups'

    id          = Column(Integer, primary_key=True)
    name        = Column(String(512))

ユーザーは複数のグループに属することができ、グループには複数のユーザーを含めることができます。

私がやろうとしているのは、グループのリストから少なくとも1つのグループに属するユーザーのみを返すSQLAlchemyクエリを作成することです。

in_関数をいじってみましたが、これはリストのメンバーシップのスカラー値をテストする場合にのみ機能するようです。私はSQLライターではないので、これに必要なSELECTステートメントの種類すらわかりません。

24
coredumperror

さて、多くの調査の結果、SQL用語に対する私自身の無知が私を妨げていることに気づきました。グループのリストの「少なくとも1つ」に属するユーザーを見つけるための解決策を探すのは、グループのリストの「いずれか」に属するユーザーを見つけることでした。 SQLAlchemyのany ORM関数は、次のように、私が必要としていたことを正確に実行します。

session.query(ZKUser).filter(ZKUser.groups.any(ZKGroup.id.in_([1,2,3])))

そのコードはこのSQLを出力します(MySQL 5.1上):

SELECT * FROM users 
WHERE EXISTS (
    SELECT 1 FROM user_groups, groups 
    WHERE users.id = user_groups.contact_id 
        AND groups.id = user_groups.group_id 
        AND groups.id IN (%s, %s, %s)
    )
43
coredumperror

any のドキュメントによると、代わりに明示的なjoinを使用すると、クエリの実行速度が速くなります。

Any()は相関サブクエリを使用するため、大きなターゲットテーブルと比較した場合、結合を使用した場合ほどパフォーマンスは向上しません。

あなたの場合、あなたは次のようなことをすることができます:

users = (
    session.query(ZKUser)
    .join(user_groups)
    .filter(user_groups.columns.group_id.in_([1, 2, 3]))
)

これにより、次のようなSQLが生成されます。

SELECT *
FROM users
JOIN user_groups ON users.id = user_groups.user_id 
WHERE user_groups.group_id IN (1, 2, 3)
11
z0r

in_を使用できます:

session.query(ZKUser).filter(ZKGroup.id.in_([1,2])).all()
0
zeekay