sqlalchemy的关联子查询

本文介绍了在使用SQLAlchemy时遇到的关联子查询问题,通过一个具体的SQL语句示例,展示了如何处理COUNT为0时丢失记录的情况。在尝试ORM和OUTER JOIN未果后,最终通过子查询和correlate、as_scalar方法成功解决。

SQLAlchemy也算是用过好几年了,不过一直都用着其中相对简单的一小部分,最近写个程序碰到个问题,需要作一个关联子查询,类似这样的SQL语句:

SELECT master.*, (
    SELECT count(*) 
    FROM detail 
    WHERE detail.parentid=master.id AND detail.someflag IS NOT NULL
) FROM master;

试了很久不知道怎么用ORM来写。

如果COUNT不为0,用下面这个查询的结果是一样的:

SELECT master.id, count(detail.id) 
FROM master 
INNER JOIN detail ON detail.parentid=master.id 
WHERE detail.someflag IS NOT NULL 
GROUP BY master.id;

这个语句倒是可以用ORM实现:

qry = orm.query(master, func.count(detail.id).join(detail, 
    detail.parentid==master.id).filter(detail.someflag!=None).group_by(master)

但是如果COUNT为0就不行了,即便用OUTER JOIN也不行,这种情况下会丢失COUNT为0的master记录。

想来想去大概只能用子查询实现,但是试了这样的语句,结果跟上面一个是一样的,也会丢失COUNT为0的master记录。

subqry = orm.query(detail.parentid, func.count(detail.id).label(
    "flagcnt").filter(detail
### SQLAlchemy 外键查询基础 在 SQLAlchemy 中,外键用于建立不同表之间的关联关系。通过 `ForeignKey` `relationship()` 方法,可以在模型之间定义这些关联,并执行基于外键的查询操作。 #### 定义外键关系 在外键定义中,通常会使用 `sqlalchemy.Column` 配合 `sqlalchemy.ForeignKey` 来指定外键约束[^3]。例如: ```python from sqlalchemy import Column, Integer, ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class Parent(Base): __tablename__ = 'parent' id = Column(Integer, primary_key=True) children = relationship("Child", back_populates="parent") class Child(Base): __tablename__ = 'child' id = Column(Integer, primary_key=True) parent_id = Column(Integer, ForeignKey('parent.id')) parent = relationship("Parent", back_populates="children") ``` 在这个例子中,`Child` 表中的 `parent_id` 列是一个外键,指向 `Parent` 表的主键列 `id`。同时,`relationship()` 函数被用来声明双向的关系[^3]。 --- #### 执行外键查询 一旦建立了外键关系,可以通过 SQLAlchemy 的 ORM 查询接口轻松访问相关数据。以下是几种常见的查询方式: ##### 1. 访问父对象的相关子对象 如果已经加载了一个父对象实例,则可以直接通过其属性获取所有相关的子对象: ```python # 假设已有一个 Parent 实例 p p.children # 返回与该 Parent 关联的所有 Child 对象列表 ``` 这利用了之前定义的 `relationship()` 属性来自动完成查询。 ##### 2. 使用显式的 JOIN 查询 对于更复杂的场景,可以手动编写带有 JOIN 子句的查询语句。例如: ```python from sqlalchemy.orm import joinedload session.query(Child).options(joinedload(Child.parent)).filter(Parent.id == some_parent_id).all() ``` 此代码片段展示了如何通过 `joinedload()` 加载关联的对象并过滤特定条件下的记录[^2]。 ##### 3. 反向查找父对象 同样地,也可以从子对象出发找到对应的父对象: ```python c = session.query(Child).first() # 获取第一个 Child 实例 if c is not None: print(c.parent) # 输出这个 Child 的 Parent 对象 ``` 这里假设存在至少一条记录以便演示反向导航的功能[^3]。 --- ### 示例:完整的外键查询流程 下面提供一个综合性的示例程序,展示如何创建数据库结构以及执行基本的外键查询操作: ```python from sqlalchemy import create_engine, Column, Integer, String, ForeignKey from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy.ext.declarative import declarative_base engine = create_engine('sqlite:///:memory:', echo=True) Session = sessionmaker(bind=engine) session = Session() Base = declarative_base() class Author(Base): __tablename__ = 'author' id = Column(Integer, primary_key=True) name = Column(String, nullable=False) books = relationship("Book", back_populates="author") # 定义关系 class Book(Base): __tablename__ = 'book' id = Column(Integer, primary_key=True) title = Column(String, nullable=False) author_id = Column(Integer, ForeignKey('author.id')) # 设置外键 author = relationship("Author", back_populates="books") # 定义逆向关系 Base.metadata.create_all(engine) # 插入测试数据 a1 = Author(name="John Doe") b1 = Book(title="SQLAlchemy Guide", author=a1) session.add(a1) session.commit() # 查询作者及其书籍 authors_with_books = session.query(Author).join(Book.author).all() for a in authors_with_books: print(f"{a.name} wrote {len(a.books)} book(s): {[b.title for b in a.books]}") ``` 上述脚本首先设置了两个实体类——`Author` `Book` 并指定了它们间的外键连接;接着插入了一些样例数据并通过联合查询检索出了每位作家所著的作品数量及名称[^4]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值