You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化SQLAlchemy查询性能?多关联表数据获取慢求助

SQLAlchemy关联多表批量数据查询性能优化方案

我用SQLAlchemy从关联多个表的Delivery表提取数据生成配送JSON文件,现在处理速度极慢——100条左右配送数据(多数包含多个任务)要花15秒。之前试过在查询里关联所有表,但没返回数据,求具体或通用的性能优化方案,相关代码如下:

# Get the deliveries for the specified date
delivery_rs = session.query(Delivery).join(Order) \
    .filter(and_(Delivery.DespatchDateTime.between(start_date, end_date), Order.ProductionSite == site_map.get(site))).all()
# Setup our rowcount for the metadata later
rowcount = 0

# Go through each delivery in the resultset, formulate the full job/delivery/client/customer data and add it to the data array
for delivery in delivery_rs:
    # Add to our rowcount
    rowcount = rowcount + 1

    # Add the jobs to our job array
    job_deliveries = delivery.JobDeliveries
    jobs = []
    quantity = 0
    for job_delivery in job_deliveries:
        job = job_delivery.Job
        web_ref = job.ClientJobReference
        if web_ref and not re.match(r'^CCW_', web_ref):
            web_ref = "" 
        elif web_ref:
            web_ref = re.sub(r'^CCW_', '', web_ref)
        jobs.append({
            "web_ref":      "CCW_{}".format(web_ref) if web_ref else "",
            "name":         job.JobName,
            # The artwork is stored in S3, so provide a link
            "thumbnail":    "https://example.com/{}.png".format(web_ref) if web_ref else ""
        })

        quantity = quantity + job_delivery.Quantity

    # Format our delivery data
    if delivery.AddressContact:
        address_contact = delivery.AddressContact
        contact_data =  {
                    "title":    title_map.get(address_contact.Title),
                    "name":     address_contact.ContactName,
                    "email":    address_contact.ContactEmail,
                    "phone":    address_contact.ContactNumber
                }
    else:
        contact_data = {}
    
    order = delivery.Order
    client = order.Client
    delivery_method = delivery.DeliveryMethod
    address = delivery.Address
    
    result["data"].append(
        {
            "order_number": order.OrderSequenceId,
            "quantity":     quantity,
            "method":       delivery_method.Name,
            "client":       client.Name,
            "end_client":   client.EndCustomer,
            "jobs":         jobs,
            "contact":      contact_data,
            "address": {
                "business": address.BusinessName,
                "postcode": address.PostCode,
                "town":     address.Town,
                "county":   address.County,
                "country":  address.Country.Name,
                "lines": [
                    address.AddressLine1,
                    address.AddressLine2
                ]
            }
        }
    )

优化建议

  • 解决N+1查询问题:用预加载关联数据
    当前循环中访问delivery.JobDeliveries、delivery.Order等关联属性时,SQLAlchemy会为每条Delivery触发单独的查询,这是性能瓶颈的核心。改用joinedload/subqueryload一次性加载所有需要的关联表:
from sqlalchemy.orm import joinedload, subqueryload

delivery_rs = session.query(Delivery).join(Order) \
    .options(
        # 预加载一对一/多对一关联
        joinedload(Delivery.Order).joinedload(Order.Client),
        joinedload(Delivery.Address).joinedload(Address.Country),
        joinedload(Delivery.DeliveryMethod),
        joinedload(Delivery.AddressContact),
        # 一对多关联用subqueryload避免结果集重复
        subqueryload(Delivery.JobDeliveries).joinedload(JobDelivery.Job)
    ) \
    .filter(and_(Delivery.DespatchDateTime.between(start_date, end_date), Order.ProductionSite == site_map.get(site))) \
    .all()
  • 确保过滤字段有数据库索引
    检查Delivery.DespatchDateTime和Order.ProductionSite是否建立了索引,没有索引会导致全表扫描,直接拖慢查询速度。

  • 简化字符串处理逻辑
    把正则替换改成更高效的字符串方法,减少CPU开销:

web_ref = job.ClientJobReference
if web_ref:
    if web_ref.startswith('CCW_'):
        web_ref = web_ref[4:]  # 直接截断比re.sub快
    else:
        web_ref = ""
  • 分批加载数据,降低内存压力
    如果后续数据量增长,不要一次性用all()取出所有数据,改用yield_per()分批加载:
delivery_rs = session.query(Delivery).join(Order) \
    .options(...)  # 保留预加载选项
    .filter(...) \
    .yield_per(50)  # 每次加载50条
  • 排查关联查询无数据的问题
    之前关联所有表没返回数据,大概率是用了内连接(join)过滤掉了关联表为空的记录。如果允许关联表为空,改用outerjoin:
delivery_rs = session.query(Delivery).outerjoin(Order) \
    .filter(...) \
    .all()

或者检查关联条件是否正确,确保业务逻辑上的关联关系和查询条件匹配。


内容的提问来源于stack exchange,提问作者James

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 22:28:09