1. join 查询

假设这样一个业务场景,知道一个邮箱地址,要查询这个地址所属的用户,第一个办法是用连接多个 filter() 来查询。

  1. for u, a in session.query(User, Address).filter(User.id==Address.user_id).filter(Address.email_address=='jack@google.com').all():
  2.  
  3. print(u)
  4. print(a)
  5.  
  6. # 执行结果
  7. jack
  8. jack@google.com

更简便的方法是使用 join() 方法:

  1. u = session.query(User).join(Address).filter(Address.email_address=='jack@google.com').one()
  2.  
  3. print(u)
  4. # 执行结果
  5. jack

Query.join() 知道如何在 UserAddress 之间进行连接,因为我们设定了外键。假如我们没有指定外键,比如这样:

  1. class User(Base):
  2. __tablename__ = 'users'
  3. id = Column(Integer, primary_key=True)
  4. name = Column(String(50))
  5. fullname = Column(String(50))
  6. password = Column(String(12))
  7.  
  8. class Address(Base):
  9. __tablename__ = 'addresses'
  10. id = Column(Integer, primary_key=True)
  11. email_address = Column(String, nullable=False)
  12. user_id = Column(Integer)

我们可以用下面方法来让 join 生效:

  1. query.join(Address, User.id==Address.user_id) # explicit condition
  2. query.join(User.addresses) # specify relationship from left to right
  3. query.join(Address, User.addresses) # same, with explicit target
  4. query.join('addresses') # same, using a string

例子:

  1. session.query(User).join(Address, User.id==Address.user_id).filter(Address.email_address=='jack@google.com').all()

2. 子查询(subquery)

现在需要查询每个用户所拥有的邮箱地址数量,思路是先对 addresses 表按用户 ID 分组,统计各组数量,这样我们得到一张新表;然后用 JOIN 连接新表和 users 两个表,在这里,我们应该使用 LEFT OUTER JOIN,因为使用 INTER JOIN 所得出的新表只包含两表的交集。

如果上面的暂时看不懂,我们先来看看第一个 stmt 的情况。

  1. from sqlalchemy.sql import func
  2.  
  3. stmt = session.query(Address.user_id, func.count('*').label('address_count')).group_by(Address.user_id).all()
  4.  
  5. for i in stmt:
  6. print(i)
  7.  
  8. # 执行结果
  9. (5, 2)

可以理解成 group_by() 方法生成了一张新的表,该表有两列,第一列是 user_id ,第二列是该 user_id 所拥有的 addresses 的数量,这个值由 func() 跟着的方法产生,我们可以使用 c() 方法来访问这个值。

  1. from sqlalchemy.sql import func
  2.  
  3. stmt = session.query(Address.user_id, func.count('*').label('address_count')).group_by(Address.user_id).subquery()
  4.  
  5. q = session.query(User, stmt.c.address_count).outerjoin(stmt, User.id==stmt.c.user_id).order_by(User.id).all()
  6.  
  7. for i in q:
  8. print(i)
  9.  
  10. # 执行结果
  11. (ed, None)
  12. (wendy, None)
  13. (mary, None)
  14. (fred, None)
  15. (jack, 2)

如果不用 outerjoin() 而使用 join(),就等于使用 SQL 中的 INTER JOIN,所得出的表只为两者交集,不会包含 None 值的列。

  1. from sqlalchemy.sql import func
  2.  
  3. stmt = session.query(Address.user_id, func.count('*').label('address_count')).group_by(Address.user_id).subquery()
  4.  
  5. q = session.query(User, stmt.c.address_count).join(stmt, User.id==stmt.c.user_id).order_by(User.id).all()
  6.  
  7. for i in q:
  8. print(i)
  9.  
  10. # 执行结果
  11. (jack, 2)

3.使用别名(aliased)

SQLAlchemy 使用 aliased() 方法表示别名,当我们需要把同一张表连接多次的时候,常常需要用到别名。

  1. from sqlalchemy.orm import aliased
  2.  
  3. # 把 Address 表分别设置别名
  4. adalias1 = aliased(Address)
  5. adalias2 = aliased(Address)
  6.  
  7. for username, email1, email2 in session.query(User.name, adalias1.email_address, adalias2.email_address).join(adalias1, User.addresses).join(adalias2, User.addresses).filter(adalias1.email_address=='jack@google.com').filter(adalias2.email_address=='j25@yahoo.com'):
  8.  
  9. print(username, email1, email2)
  10.  
  11. # 执行结果
  12. jack jack@google.com j25@yahoo.com

上述代码查询同时拥有两个名为:"jack@google.com" 和 "j25@yahoo.com" 邮箱地址的用户。

别名也可以在子查询里使用:

  1. from sqlalchemy.orm import aliased
  2.  
  3. stmt = session.query(Address).filter(Address.email_address != 'j25@yahoo.com').subquery()
  4.  
  5. adalias = aliased(Address, stmt)
  6.  
  7. for user, address in session.query(User, adalias).join(adalias, User.addresses):
  8.  
  9. print(user)
  10. print(address)
  11.  
  12. # 执行结果
  13. jack
  14. jack@google.com

4. EXISTS 关键字

EXISTS 关键字可以在某些场景替代 JOIN 的使用。

  1. from sqlalchemy.sql import exists
  2.  
  3. stmt = exists().where(Address.user_id==User.id)
  4.  
  5. for name, in session.query(User.name).filter(stmt):
  6. print(name)
  7.  
  8. # 执行结果
  9. jack

使用 any() 方法也能得到同意的效果:

  1. for name, in session.query(User.name).filter(User.addresses.any()):
  2.  
  3. print(name)

使用 any() 方法时也可加上查询条件:

  1. for name, in session.query(User.name).filter(User.addresses.any(Address.email_address.like('%google%'))):
  2. print(name)

使用 has() 方法也能起到 JOIN 的作用:

  1. session.query(Address).filter(~Address.user.has(User.name=='jack')).all()

注意:这里的 ~ 符号是 “不” 的意思。


关系运算符

1. 等于、不等于

  1. query = session.query(Address)
  2. jack = session.query(User).filter(User.name == 'jack').one()
  3.  
  4. # 筛选 user 为 jack 的邮箱
  5. query.filter(Address.user == jack)
  6.  
  7. # 筛选 user 不为 jack 的邮箱
  8. query.filter(Address.user != jack)

2. 为空、不为空

  1. # 筛选 user 为空的邮箱
  2. query.filter(Address.user == None)
  3.  
  4. # 筛选 user 不为空的邮箱
  5. query.filter(Address.user != None)

3. 包含

  1. query = session.query(User)
  2. address = session.query(Address).filter(Address.id == 1).one()
  3.  
  4. # 筛选包含某地址的用户
  5. query.filter(User.addresses.contains(address))

flask_SQLALchemy之多表查询的更多相关文章

  1. django(3) 一对多跨表查询、ajax、多对多

    1.一对多跨表查询获取数据的三种形式:对象.字典.元组 例:有host与business两张表,host与business的id字段关联,business在host表中的对象名是b,  通过查询hos ...

  2. Mysql常用表操作 | 单表查询

    160905 常用表操作 1. mysql -u root -p 回车 输入密码   2. 显示数据库列表 show databases     3. 进入某数据库 use database data ...

  3. Oracle_多表查询

    SQL多表查询 等值和不等值连接查询 从多个表中获取数据:如果在查询的时候,直接从多个表中获取数据.没有添加条件判断,会出现"笛卡尔积"错误 笛卡尔积错误 笛卡尔集会在下面条件下产 ...

  4. mysql,SQL标准,多表查询中内连接,外连接,自然连接等详解之查询结果集的笛卡尔积的演化

    先附上数据. CREATE TABLE `course` ( `cno` ) NOT NULL, `cname` ) CHARACTER SET utf8 NOT NULL, `ctime` ) NO ...

  5. MyBatis实现关联表查询

    一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关 ...

  6. MyBatis学习总结(五)——实现关联表查询(转载)

    本文转载自:http://www.cnblogs.com/jpf-java/p/6013516.html 一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数 ...

  7. sql多表查询时怎么获取查到的字段

    首先,多表查询不能用hql(貌似hql就是不支持多表查询,如果可以,希望看到的朋友给个例子) List list = systemService.findListbySql("SELECT ...

  8. MyBatis入门学习教程-实现关联表查询

    一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关 ...

  9. 【T-SQL基础】01.单表查询-几道sql查询题

    概述: 本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础 ...

随机推荐

  1. maven的单元测试中没有

    原因:BaseTest没有找到单元测试造成的 增加一个空的单元测试 @Testpublic void testNothing(){} 异常现象:在maven项目执行mvn install 或mvn t ...

  2. C#对接JAVA系统遇到的AES加密坑

    起因对接合作伙伴的系统,需要对数据进行AES加密 默认的使用了已经写好的帮助类中加密算法,发现结果不对,各种尝试改变加密模式改变向量等等折腾快一下午.最后网上查了下AES在JAVA里面的实现完整代码如 ...

  3. A bean with that name has already been defined in DataSourceConfiguration$Hikari.class

    A bean with that name has already been defined in DataSourceConfiguration$Hikari.class 构建springcloud ...

  4. mac下安装cnpm淘宝镜像

    cnpm:官网 (事先已经安装了node,有npm)查看官网,提示安装需运行命令:npm install -g cnpm --registry=https://registry.npm.taobao. ...

  5. HTML 元素大小

    1.元素的偏移量 元素的可见大小是由其高度.宽度决定,包括所有的内边距.滚动条和边框大小(不包括外边距). offsetHeight :元素在垂直方向上占用的空间大小,以像素计算.包括元素的高度,水平 ...

  6. python 路径处理

    1.分解路径名 比如要把xxx/yyy/zzz.py 分解成文件名和目录 两种方法: 一.os.path.split(file) 二.os.path.basename()  ;   os.path.d ...

  7. Python设计模式 - 基础 - 类/接口之间的六种关系

    在程序中需要把世间万物抽象成相应的类,现实世界中物与物之间的关系和程序中类与类之间的关系相对应,因为世间万物是普遍联系的,所以程序中类与类之间也不是孤立的.在系统分析和框架设计中,根据面向对象机制的三 ...

  8. Ubuntu修改时间时区

    设定时区:dpkg-reconfigure tzdata 选择Asia,Hong Kong. sudo apt-get install ntpdate // 安装时间同步工具 sudo ntpdate ...

  9. TZOJ 2099 Sightseeing tour(网络流判混合图欧拉回路)

    描述 The city executive board in Lund wants to construct a sightseeing tour by bus in Lund, so that to ...

  10. Linux磁盘空间分析及清理(df、du、rm)

    1.df磁盘空间查看 df可以查看一级文件夹大小.使用比例.档案系统及其挂入点. [root@oms ~]# df -Th Filesystem Type Size Used Avail Use% M ...