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

Peewee避免子查询扁平化 替换RIGHT JOIN适配SQLite方案

1. Peewee子查询被扁平化的原因

Peewee内置的查询优化器默认会对可合并的嵌套子查询执行扁平化操作,核心目的是降低SQL的嵌套层级,减少数据库执行计划的开销,提升查询效率。
只有当子查询包含GROUP BY、LIMIT、DISTINCT、聚合函数这类特殊语法,或者你显式标记子查询为独立查询源时,Peewee才会保留子查询的别名和独立结构。你遇到的peewee.ProgrammingError: missing FROM-clause entry for table "solutions_subquery"报错,就是优化器判定你的子查询逻辑可以合并到外层查询,直接移除了子查询别名并展开逻辑,导致你在外层关联时引用的别名失效。

2. 兼容SQLite的LEFT JOIN实现方案

原来的RIGHT JOIN逻辑完全可以等价转换为LEFT JOIN逻辑:A RIGHT JOIN B和B LEFT JOIN A返回的结果完全一致,只需要交换主表和关联表的顺序即可,同时可以通过以下两种方案避免子查询被扁平化:

方案1:使用CTE定义子查询(更稳定)

Peewee不会对公共表表达式(CTE)做扁平化处理,能100%保留你指定的别名,示例代码如下:

from peewee import JOIN, fn

# 定义获取每个用户+习题最新解答ID的子查询,标记为CTE
solutions_subquery = (
    Solution
    .select(
        Solution.user_id,
        Solution.problem_id,
        fn.MAX(Solution.id).alias("latest_solution_id")
    )
    # 保留你原来的关联、过滤逻辑
    .join(User)
    .join(Problem)
    .where(/* 你的自定义范围过滤条件 */)
    .group_by(Solution.user_id, Solution.problem_id)
    .cte("solutions_subquery")
)

# 以子查询为基础左关联解答表、评分表
final_query = (
    solutions_subquery
    .select(
        solutions_subquery.c.user_id,
        solutions_subquery.c.problem_id,
        Solution,
        SolutionAssessment.score_icon
    )
    .join(
        Solution,
        on=(Solution.id == solutions_subquery.c.latest_solution_id),
        join_type=JOIN.LEFT_OUTER
    )
    .join(
        SolutionAssessment,
        on=(SolutionAssessment.solution_id == Solution.id),
        join_type=JOIN.LEFT_OUTER
    )
    .with_cte(solutions_subquery)
)

该方案生成的SQL全程使用LEFT JOIN,完全兼容SQLite,返回结果和你原来的RIGHT JOIN逻辑完全一致,无提交的记录对应解答、评分字段会自动返回null。

方案2:强制指定子查询为FROM源

如果不想用CTE,也可以通过from_方法强制Peewee把子查询作为独立的查询源,避免扁平化:

solutions_subquery = (
    Solution
    .select(
        Solution.user_id,
        Solution.problem_id,
        fn.MAX(Solution.id).alias("latest_solution_id")
    )
    .where(/* 过滤条件 */)
    .group_by(Solution.user_id, Solution.problem_id)
    .alias("solutions_subquery")
)

final_query = (
    Solution
    .select(
        solutions_subquery.c.user_id,
        solutions_subquery.c.problem_id,
        Solution,
        SolutionAssessment.score_icon
    )
    .from_(solutions_subquery) # 核心:强制把子查询作为FROM源,禁止扁平化
    .join(
        Solution,
        on=(Solution.id == solutions_subquery.c.latest_solution_id),
        join_type=JOIN.LEFT_OUTER
    )
    .join(
        SolutionAssessment,
        on=(SolutionAssessment.solution_id == Solution.id),
        join_type=JOIN.LEFT_OUTER
    )
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 23:54:04