首页 > 数据库 >[SQLAlchemy] sqlAlchemy学习笔记(3): 在orm中使用join

[SQLAlchemy] sqlAlchemy学习笔记(3): 在orm中使用join

时间:2024-01-26 11:33:47浏览次数:25  
标签:account sqlAlchemy join email orm user address id User

JOIN连接表

➡️官方文档⬅️
在sqlalchemy中实现JOIN/ON语句, 需要用到Select.join()或者Select.join_from()

假设我们有两张表 (在python中就变成了两个类) User和Address, User表中有一列addresses, 表示该用户使用的所有地址的集合 (反映到代码中, 就是Address对象的集合); 同时Address表中也有一列user_id, 对应User表中的id字段

使用relationship()来进行关联
有关relationship()的内容看➡️这里
本文中还是使用user_account和address这两个表, 以及User和Address这两个类, User.id和Address.user_id为主/外键关联, User.addresses和Address.user之间使用relationship()关联

JOIN的基本使用方法

先来看看在sql中我们如何写join语句

-- 假设: 返回用户和他们的所有地址
SELECT * FROM user_account u
JOIN address a ON u.id = a.user_id;

注意JOIN...ON...是必备的组合, 但是在sqlalchemy中, ON语句可以被自动推断出来, 前提是对应类中有配置相应的主/外键属性

# 在user/address案例中, id/user_id为主/外键
print(select(User).join(Address))

对应的sql语句为

SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account 
JOIN address ON user_account.id = address.user_id;

这里没有指定ON语句后的条件, 但join方法会自动根据外键来生成

指定条件

⬇️这是在join方法里指定了ON条件, 注意我们使用的是表达式

print(select(User).join(Address, User.id == Address.user_id))

生成的sql语句是一样的, 但是这里指定了ON条件

增加条件

⬇️我们也可以通过PropComponent._and()来增加条件名字好奇怪
注意写法User.addresses.and_(...), and_()是属于主表(左表)里由relationship()绑定的那个

print(select(User.fullname).join(
    User.addresses.and_(Address.email_address == "[email protected]")
))

sql

SELECT user_account.fullname
FROM user_account
JOIN address ON user_account.id = address.user_id AND address.email_address = '[email protected]';

and_()方法和relationship()在某种程度上挂钩

使用别名

在这里我们以可以用别名区分表(类)

# 取个别名
a1 = aliased(Address)
a2 = aliased(Address)
# 查询语句
stmt = (
    select(User)
    .join(a1, User.addresses)
    .where(a1.email_address == "[email protected]")
    .join(a2, User.addresses)
    .where(a2.email_address == "[email protected]")
)

sql

  1. 重复使用where的时候会自动由AND相连
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
JOIN address AS a1 ON user_account.id = a1.user_id
JOIN address AS a2 ON user_account.id = a2.user_id
WHERE a1.email_address = :email_address_1 AND a2.email_address = :email_address_2;

使用子查询

Select.subquery()

subq = select(Address).where(Address.email_address == "[email protected]").subquery()
address_subq = aliased(Address, subq, name="address")
stmt = select(User, address_subq).join(address_subq)
for row in session.execute(stmt):
    print(f"{row.User} {row.address}")

上面这段代码对应的sql是这样的

-- 返回邮箱为'[email protected]'的用户信息和特定邮箱地址
SELECT user_account.id, user_account.name, user_account.fullname, a1.id AS id_1, a1.user_id, a1.email_address
FROM user_account
JOIN 
   (
   -- 返回邮箱地址为'[email protected]'的行
    SELECT address.id AS id,
           address.user_id AS user_id,
           address.email_address AS email_address
    FROM address
    WHERE address.email_address = '[email protected]'
    ) AS a1 
ON user_account.id = a1.user_id;

User(id=3, name='patrick', fullname='Patrick Star') Address(id=4, email_address='[email protected]')

或者用relationship()改进一下

subq = select(Address).where(Address.email_address == "[email protected]").subquery()
address_subq = aliased(Address, subq, name="address")
stmt = select(User, address_subq).join(User.addresses.of_type(address_subq))
for row in session.execute(stmt):
    print(f"{row.User} {row.address}")

对应的sql是一样的

-- 返回所有用户及其地址, 每个地址都是一个单独行
SELECT user_account.id, user_account.name, user_account.fullname, a2.id AS id_1, a2.user_id, a2.email_address
FROM user_account
JOIN 
  (SELECT address.id AS id,
          address.user_id AS user_id, 
          address.email_address AS email_address
   FROM address
   WHERE address.email_address = '[email protected]'
   ) AS a2 
ON user_account.id = a2.user_id;

甚至可以在一个子查询里引用两个类

子查询

user_address_subq = (
    select(User.id, User.name, User.fullname, Address.id, Address.email_address)
    .join_from(User, Address)
    .where(Address.email_address.in_(["[email protected]", "[email protected]"]))
    .subquery()
)

别名, 这样一个子查询就分别应用在两个类(实例)上了

user_alias = aliased(User, user_address_subq, name="user")
address_alias = aliased(Address, user_address_subq, name="address")

执行时, 两个实例的子查询会同时执行, 但是结果会被同时写入结果中

stmt = select(user_alias, address_alias).where(user_alias.name == "sandy")
for row in session.execute(stmt):
    print(f"{row.user} {row.address}")

sql

SELECT anon_1.id, anon_1.name, anon_1.fullname, anon_1.id_1, anon_1.email_address
FROM 
    (
       SELECT user_account.id AS id, user_account.name AS name, user_account.fullname AS fullname, address.id AS id_1,
address.email_address AS email_address
       FROM user_account 
       JOIN address ON user_account.id = address.user_id 
       WHERE address.email_address IN '[email protected]', '[email protected]')
    ) AS anon_1
WHERE anon_1.name = 'sandy';

标签:account,sqlAlchemy,join,email,orm,user,address,id,User
From: https://www.cnblogs.com/Akira300000/p/17980344

相关文章

  • HTTP 请求体编码用 json 还是 x-www-form-urlencoded
    application/x-www-form-urlencodedapplication/jsonapplication/json对初学者友好application/x-www-form-urlencoded对Postman友好axios和superagent默认使用JSONbody来自专家的建议TheStripeAPIisorganizedaroundREST.OurAPIhaspredictableresour......
  • [sqlAlchemy] sqlAlchemy中的relationship()
    在学习select和join的时候被relationship相关的东西搞得头大,感觉看不懂这个就没办法继续下去了官方教程在这里先把我们一直在用的两个类拿过来用户类,对应user_account表classUser(Base):__tablename__="users"id:Mapped[int]=mapped_column(primary_key=T......
  • Java中SimpleDateFormat时YYYY与yyyy以及HH和hh的区别注意踩坑
    场景Java开发手册中为什么要求SimpleDateFormat时用y表示年,而不能用Y:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/131052335在使用SimpleDateFormat在获取当前日期时因使用了YYYY导致20231231这个日期被格式化为20241231这里推荐在日期处理时统一使用封装工具......
  • A Format Compliant Encryption Method for 3D Objects Allowing Hierarchical Decryp
    Abstract—Withtheincreasingpopularityof3Dobjectsinindustryandeverydaylife,3Dobjectsecurityhasbecomeessential.Whilethereexistsmethodsfor3Dselectiveencryption,whereaclear3Dobjectisencryptedsothattheresulthasthedesiredl......
  • Error: unable to perform an operation on node 'rabbit@pro'. Please see diagnosti
    简短的和全限定RabbitMQ节点名称rabbitmq支持简短的和全限定域名作为节点名称,但是默认的是简短的,我这里使用了全限定的域名,所以在集群操作stop_app的时候报错了  在rabbitmq安装目录下的/etc/rabbitmq加上配置文件rabbitmq-env.conf(环境变量)就可以了#开启使用全限定节点名......
  • jeecg-boot 同步数据库失败,Unable to perform unmarshalling at line number 5 and co
    同步数据库失败,Unabletoperformunmarshallingatlinenumber5andcolumn6.Message:cvc-complex-type.2.4.a:Invalidcontentwasfoundstartingwithelement'{"http://www.hibernate.org/xsd/orm/hbm":property}'.Oneof'{"http://www......
  • python中(“{}{}{}”.format(i,j,k))的理解
    “{}{}{}”.format(i,j,k)笼统的来说是字符串的格式化字符串中有一些可以被替换掉的占位符,而格式化的过程就是对这些占位符替换的过程,举例来说:1“Iama{}”.format("student")它表示将字符串"Iama{}"进行格式化,格式化的结果就是该字符串中的占位符{}被format()函数中的参......
  • webstorm报错:ESLint: TypeError: this.libOptions.parse is not a function
     解决办法:把 {**/*,*}.{js,ts,jsx,tsx,html,vue}换成{**/*,*}.(js,ts,jsx,tsx,html,vue) 原文:https://stackoverflow.com/questions/73509984/eslint-typeerror-this-liboptions-parse-is-not-a-functionMyfriends,ifyouareusing Webstorm orany Jetbrains p......
  • WebForm程序中集成Mvc4的方法
    本文为大家分享了asp.net4.0+webform程序中集成mvc4的方法,供大家参考,具体内容如下新建packages.config文件,里面加上必要的程序集<?xmlversion="1.0"encoding="utf-8"?><packages><packageid="Microsoft.AspNet.Mvc"version="4.0.20710.0&q......
  • NSIS打包教程 Winform程序打包
    NSIS软件下载地址:https://pan.baidu.com/s/1sbBX__7Q4ntHeEDp-yl1ng提取码:xeax相关的视频教程https://www.bilibili.com/video/BV1jf4y1a7ji?share_source=copy_web(如果不会请看我录制的视频教程)NSIS打包教程Wnform程序打包,,安装部署步骤如下图 桌面快......