postgresql - SQLAlchemy:在多对多关系上执行 LEFT JOIN 时的复杂 ON 子句

标签 postgresql sqlalchemy many-to-many left-join outer-join

在对多对多关系执行 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 列表是由外部代码创建的,所以我不能用它来构建 primaryjoinsecondaryjoin 关系过滤器,允许的 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/

相关文章:

c# - 使用 Entity Framework ,我如何反射(reflect)多对多关系并将存在的实体添加到正在创建的新实体?

postgresql - Postgres - 限制每个 session 运行的程序

sql - PostgreSQL 9.3 获取已有表中元组的插入时间

hibernate - 主键与约束 pk_id 主键(id)

python - SQLAlchemy - 批量插入忽略重复/唯一

python - SQLAlchemy Automap 不会为没有主键的表创建类

java - hibernate : StackOverflowException logging ManyToMany association

sql - 如何复制 Redshift 表但向列添加排序键?

python - 将 sqlalchemy 查询结果转换为字典列表

Django - 在管理员之外使用多对多水平界面