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

SQLAlchemy AsyncSession查询报错:需调用Result.unique()方法求因

SQLAlchemy AsyncSession 关联集合查询错误分析

问题背景

模型类定义

import enum
import uuid
from sqlalchemy import Column, UUID, String, Boolean, DateTime, ForeignKey, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from typing import List, Any

Base = declarative_base()

class PricingFrequency(enum.Enum):
    month = 'month'
    year = 'year'


class PlanPricing(Base):
    __tablename__ = "PlansPricing"
    pricing_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    # 省略其他字段
    subscription_plan = relationship("SubscriptionPlan", back_populates="plans_pricing")
    plan_id = Column(UUID(as_uuid=True), ForeignKey("SubscriptionPlans.plan_id"))

    created_on = Column(DateTime, server_default=func.now())
    updated_on = Column(DateTime, server_default=func.now(), server_onupdate=func.now())


class SubscriptionPlanOption(Base):
    __tablename__ = "SubscriptionPlanOptions"
    option_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    # 省略其他字段
    subscription_plan = relationship("SubscriptionPlan", back_populates="options_plan")
    plan_id = Column(UUID(as_uuid=True), ForeignKey("SubscriptionPlans.plan_id"))

    created_on = Column(DateTime, server_default=func.now())
    updated_on = Column(DateTime, server_default=func.now(), server_onupdate=func.now())


class SubscriptionPlan(Base):
    __tablename__ = "SubscriptionPlans"
    plan_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    plan_name = Column(String)
    plan_description = Column(String)
    is_popular = Column(Boolean, default=False)

    plans_pricing: List[Any] = relationship("PlanPricing", back_populates="subscription_plan")
    options_plan: List[Any] = relationship("SubscriptionPlanOption", back_populates="subscription_plan")

    created_on = Column(DateTime, server_default=func.now())
    updated_on = Column(DateTime, server_default=func.now(), server_onupdate=func.now())

查询代码

# 假设pricing_id是已定义的变量
query = (
    select(SubscriptionPlan)
    .options(
        joinedload(SubscriptionPlan.options_plan, innerjoin=True),
        joinedload(SubscriptionPlan.plans_pricing.and_(PlanPricing.pricing_id == pricing_id), innerjoin=True)
    )
)
items = await session.execute(query)
items = items.scalars().all()

错误信息

The unique() method must be invoked on this Result, as it contains results that include joined eager loads against collections

问题根源

当使用joinedload同时加载两个或多个集合类型的关联关系(这里options_plan和plans_pricing都是一对多关联)时,SQL查询会生成笛卡尔积结果:同一个SubscriptionPlan实例会对应多行数据库记录(行数等于该Plan关联的Pricing数量 × Option数量)。

SQLAlchemy的scalars()方法默认不会自动处理重复的实体实例,因为它无法判断是否需要保留重复结果。为了确保返回的每个SubscriptionPlan实例唯一,必须显式调用unique()方法,让SQLAlchemy根据实体主键识别并合并重复的实体对象。

解决方法

在调用scalars()之前添加unique()方法:

items = await session.execute(query)
items = items.unique().scalars().all()

unique()会基于实体主键对结果去重,确保每个SubscriptionPlan只返回一次,同时正确加载对应的关联集合数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 04:55:18