新手教程系列 -- SQLAlchemy对同一张表联表两次

21 篇文章 0 订阅
10 篇文章 0 订阅

在开发过程中,我们经常会遇到对同一张表进行多次联表查询的需求。比如在查询航线时,我们希望将起飞和降落的机场名称代入结果中。为了实现这一目标,机场名称统一存放在 AirPort 表中。下面,我们将介绍如何通过 SQLAlchemy 实现这一需求。

问题描述

一般情况我们第一时间会想到这么写:

from sqlalchemy import select
async with get_db_session_async() as session:
    stmt = (
        select(
            AirRoute.dep_airport_code,
            AirPort.airport_en_name.label('dep_airport_name'),
            AirRoute.arr_airport_code,
            AirPort.airport_en_name.label('arr_airport_name'),
            ResourceSite.site_code,
            ResourceSite.site_name,
            ResourceSite.flight_code,
        )
        .join(ResourceSite, AirRoute.resource_site_id == ResourceSite.id, isouter=True)
        .join(AirPort, AirRoute.dep_airport_code == AirPort.airport_code, isouter=True)
        .join(AirPort, AirRoute.arr_airport_code == AirPort.airport_code, isouter=True)
        .limit(1)
    )
    row_set = await session.execute(stmt)
    air_route_rows = [row._asdict() for row in row_set.all()]

然而,运行上面的代码时,会得到一个错误:

OperationalError: (pymysql.err.OperationalError) (1066, "Not unique table/alias: 'air_ports'")

这个错误是由于在同一个查询中多次引用同一张表 AirPort,导致表别名冲突。

解决方案:使用别名(aliased)

为了解决上述问题,我们可以使用 SQLAlchemy 提供的 aliased 方法。通过为同一张表创建不同的别名,可以避免表别名冲突。

  • 注意:不同版本的 SQLAlchemy 可能 aliased 存放的包路径不一样,具体请以您使用的 SQLAlchemy 版本为准
from sqlalchemy import select
from sqlalchemy.orm import aliased

async with get_db_session_async() as session:
    dep_airports_table = aliased(AirPort, name='dep_airports_table')
    arr_airports_table = aliased(AirPort, name='arr_airports_table')
    stmt = (
        select(
            AirRoute.dep_airport_code,
            dep_airports_table.airport_en_name.label('dep_airport_name'),
            AirRoute.arr_airport_code,
            arr_airports_table.airport_en_name.label('arr_airport_name'),
            ResourceSite.site_code,
            ResourceSite.site_name,
            ResourceSite.flight_code,
        )
        .join(ResourceSite, AirRoute.resource_site_id == ResourceSite.id, isouter=True)
        .join(dep_airports_table, AirRoute.dep_airport_code == dep_airports_table.airport_code, isouter=True)
        .join(arr_airports_table, AirRoute.arr_airport_code == arr_airports_table.airport_code, isouter=True)
        .limit(1)
    )
    row_set = await session.execute(stmt)
    air_route_rows = [row._asdict() for row in row_set.all()]

通过为 AirPort 表生成两个别名对象 depairportstable 和 arrairportstable,我们可以分别进行联表查询,避免了别名冲突问题。

最终结果

经过修改,我们成功对 AirPort 表生成了两个别名对象,并通过这两个对象进行联表查询,得到了正确的结果:

[{'dep_airport_code': 'ADB', 'dep_airport_name': 'Adnan Menderes Airport', 'arr_airport_code': 'SIN', 'arr_airport_name': 'Singapore Changi Airport', 'site_code': 'xxx', 'site_name': 'Example Site', 'flight_code': 'XX123'}]

通过以上步骤,我们解决了 SQLAlchemy 对同一张表进行多次联表查询时的别名冲突问题,使查询结果更加准确和直观。

结论

在开发中,对同一张表进行多次联表查询是一个常见需求。通过使用 SQLAlchemy 的 aliased 方法,可以有效避免表别名冲突问题,从而实现预期的查询结果。希望这篇教程能帮助你更好地理解和应用 SQLAlchemy 进行复杂查询。

关注【程序员的开发手册】,让您少走十年弯路!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值