在对多对多关系执行 JOIN 时,有什么方法可以使用动态过滤器扩展 ON
子句吗?
我在 StackOverflow 上找到了很好的相关问题:“Performing a left join across a many-to-many table with conditions”。不幸的是,这个问题是关于原始 SQL 的,只是描述了同样的问题。我还没有找到任何好的解决方案如何在 SQLAlchemy 中实现它。
好吧,让我描述一下环境。假设有两个表:
Model = declarative_base()
M2M = Table('m2m', Model.metadata,
Column('book_id', Integer, ForeignKey(...)),
Column('author_id', Integer, ForeignKey(...)),
class Book(Model):
__tablename__ = 'books'
id = Column(Integer, primary_key=True, autoincrement=True)
authors = relation('Author',
secondary=M2M,
back_populates='books')
class Author(Model):
__tablename__ = 'authors'
id = Column(Integer, primary_key=True, autoincrement=True)
books = relation('Book',
secondary=M2M,
back_populates='authors')
让我们创建一些作者,然后将一些书分配给他们:
A1 = Author() # id: 1
A2 = Author() # id: 2
A3 = Author() # id: 3
B1 = Book(authors=[A1, A2]) # id: 1
B2 = Book(authors=[A2]) # id: 2
B3 = Book(authors=[A3]) # id: 3
好吧,现在我想获得 ID 在 [1,3] 中的 Books 与 ID 在 [1,2] 中的外部加入的作者。我的意思是,如果作者 ID 在某种“允许的 ID”列表中,我想获得加载了作者的书籍 B1 和 B2。这种允许的 id 列表是由外部代码创建的,所以我不能用它来构建 primaryjoin
或 secondaryjoin
关系过滤器,允许的 id 列表是在外部动态生成的。
根据 this question ,以下查询会有所帮助:
SELECT books.id, authors.id
FROM books
LEFT JOIN m2m ON (
books.id = m2m.book_id
AND m2m.author_id IN (1,2)) -- allowed author ids!
LEFT JOIN authors ON author.id = m2m.author_id
WHERE books.id IN (1,3) -- and any another filter
;
-- result:
-- books.id | authors.id
-- ---------+-----------
-- 1 | 1
-- 1 | 2
-- 3 | null
但我不知道如何使用 SQLAlchemy 创建这样的查询。该文档描述了仅基于子查询的解决方案。
如果有任何帮助,我将不胜感激。
最佳答案
book_ids = [1, 3]
author_ids = [1, 2]
q = (session.query(Book.id, Author.id)
.outerjoin(M2M, and_(Book.id == M2M.c.book_id, M2M.c.author_id.in_(author_ids)))
.outerjoin(Author, M2M.c.author_id == Author.id)
.filter(Book.id.in_(book_ids))
)
但是,我认为下面应该产生相同的结果,但少一个 JOIN
:
q = (session.query(Book.id, M2M.c.author_id)
.outerjoin(M2M, and_(Book.id == M2M.c.book_id, M2M.c.author_id.in_(author_ids)))
.filter(Book.id.in_(book_ids))
)
关于postgresql - SQLAlchemy:在多对多关系上执行 LEFT JOIN 时的复杂 ON 子句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25868681/