Django ORM 语法 ↔ SQL 连接类型对照表
已于 2026年02月02日 15:40 修改
访问次数:0
Django ORM ↔ SQL JOIN 对照表(工程版)
结论基于 Django 官方 ORM 行为 + 实际生成 SQL
一、最常见的 6 种情况(你日常 90% 会遇到)
| Django ORM 写法 | SQL JOIN 类型 | 条件位置 | 说明 |
|---|---|---|---|
annotate(Count('rel')) | LEFT OUTER JOIN | HAVING | 保留 0 条关联记录 |
annotate(Count('rel')).filter(count=0) | LEFT OUTER JOIN | HAVING | 查“没有关联记录”的对象 |
filter(rel__field=value) | INNER JOIN | WHERE | 只保留有关联记录 |
filter(rel__isnull=False) | INNER JOIN | WHERE | 等价于存在关联 |
filter(rel__isnull=True) | LEFT OUTER JOIN | WHERE | 查“没有关联”的对象 |
exclude(rel__field=value) | LEFT OUTER JOIN | WHERE + NOT | Django 为保持语义会用 LEFT |
二、ForeignKey 正向 / 反向(你刚才讨论的核心)
正向 ForeignKey(Book → Author)
Book.objects.select_related('author')
| 项目 | 行为 |
|---|---|
| JOIN | INNER JOIN |
| 原因 | 外键字段非空(默认) |
| 特点 | 单条 SQL |
反向 ForeignKey(Author → Book)
Author.objects.prefetch_related('book_set')
| 项目 | 行为 |
|---|---|
| JOIN | ❌ 无 JOIN |
| 查询数 | 2 条 SQL |
| 原因 | Python 层合并 |
三、annotate 场景(非常重要)
统计关联数量
Author.objects.annotate(book_count=Count('book_set'))
| 项目 | 行为 |
|---|---|
| JOIN | LEFT OUTER JOIN |
| GROUP BY | 主键 |
| 目的 | 保留 book_count = 0 |
统计 + 过滤
Author.objects.annotate(book_count=Count('book_set')).filter(book_count__gt=3)
| 项目 | 行为 |
|---|---|
| JOIN | LEFT OUTER JOIN |
| 条件 | HAVING |
| 语义 | 按统计结果过滤 |
四、isnull 是 JOIN 选择的分水岭(必背)
查“有关联”的对象
Author.objects.filter(book__isnull=False)
| 项目 | 行为 |
|---|---|
| JOIN | INNER JOIN |
| 原因 | 必须存在 book |
查“没有关联”的对象
Author.objects.filter(book__isnull=True)
| 项目 | 行为 |
|---|---|
| JOIN | LEFT OUTER JOIN |
| WHERE | book.id IS NULL |
五、exclude 的特殊性(很多人会踩坑)
Author.objects.exclude(book__price__gt=100)
| 项目 | 行为 |
|---|---|
| JOIN | LEFT OUTER JOIN |
| 原因 | Django 需要保留“没有 book 的 author” |
六、select_related vs prefetch_related(JOIN 视角)
| ORM | JOIN | 适用关系 |
|---|---|---|
select_related | INNER JOIN | FK / OneToOne |
prefetch_related | 无 JOIN | 反向 / M2M |
七、超短记忆口诀(工程必背)
统计 → LEFT JOIN普通过滤 → INNER JOINisnull=True → LEFT JOINisnull=False → INNER JOINprefetch → 不 JOIN
八、提醒(非常重要)
Django ORM 没有 RIGHT JOIN / FULL JOIN
如果你需要:
- FULL OUTER JOIN
- RIGHT JOIN
👉 必须:
- raw()
- 数据库视图
- 或重构模型 / 查询逻辑
评论(0)