orm详解--查询执行

深入解析 Django ORM 查询执行阶段 的核心机制,包括查询集的惰性特性、表达式树构建、SQL 编译过程及优化原理。以下是详细分析:

一、查询集(QuerySet)的惰性执行机制

1. 惰性特性的底层实现
  • 核心类django.db.models.query.QuerySet

  • 关键属性

    • query:存储查询逻辑的 django.db.models.sql.Query 对象
    • _result_cache:结果缓存(初始为 None,首次求值后填充)
  • 惰性触发逻辑

    python 复制代码
    # 创建查询集(未执行 SQL)
    qs = Book.objects.filter(author="Doubao")
    
    # qs.query 对象此时包含:
    # {
    #   'model': Book,
    #   'where': [AND, =(author, "Doubao")],
    #   'limit': None,
    #   'offset': None,
    #   'order_by': []
    # }
2. 链式操作原理
  • 每次调用 filter()exclude()order_by() 等方法时:

    1. 创建新的 QuerySet 对象
    2. 复制原 QuerySetquery 对象
    3. 在新 query 对象上添加操作
  • 示例代码

    python 复制代码
    qs1 = Book.objects.filter(author="Doubao")  # QuerySet A
    qs2 = qs1.order_by("-price")               # QuerySet B(复制 QuerySet A 的 query 并添加排序)
    
    # qs2.query 现在包含:
    # {
    #   'model': Book,
    #   'where': [AND, =(author, "Doubao")],
    #   'order_by': ["-price"]
    # }

二、查询表达式树(Query Expression Tree)

1. 核心组件
  • WhereNode :表示 SQL 的 WHERE 子句
    • 操作类型:ANDORNOT
    • 子节点:可以是其他 WhereNode 或具体条件
  • Lookup :表示具体的查询条件(如 author="Doubao"
    • 包含:字段、操作符(如 exactgt)、值
2. 复杂查询树构建示例
python 复制代码
# Python 查询
books = Book.objects.filter(
    Q(author="Doubao") & Q(price__gt=50) | Q(published_date__year=2023)
)

# 对应的表达式树结构(简化表示):
# WhereNode(connector=OR):
#   ├─ WhereNode(connector=AND):
#   │    ├─ Lookup(field=author, lookup=exact, value="Doubao")
#   │    └─ Lookup(field=price, lookup=gt, value=50)
#   └─ Lookup(field=published_date__year, lookup=exact, value=2023)

三、SQL 编译过程

1. 触发编译的操作

以下方法会触发 SQL 编译和执行:

  • 迭代操作for book in queryset
  • 切片操作queryset[0:10](带步长的切片会强制求值)
  • 聚合方法count()exists()aggregate()
  • 获取单个对象first()last()get()
2. SQLCompiler 工作流程
  • 核心类django.db.backends.*.compiler.SQLCompiler
  • 编译步骤
    1. 生成 SELECT 列表 :根据 values() 或模型字段确定返回列
    2. 构建 FROM 子句:处理表名和别名
    3. 编译 WHERE 子句 :将 WhereNode 转换为 SQL 条件
    4. 处理 JOIN:根据关联关系生成 INNER JOIN/LEFT OUTER JOIN
    5. 添加 LIMIT/OFFSET:处理分页逻辑
    6. 生成 ORDER BY :根据 order_by() 生成排序规则
3. 参数化查询实现
  • 所有动态值通过占位符传递,防止 SQL 注入:

    python 复制代码
    # Python 查询
    Book.objects.filter(title__contains="Django")
    
    # 生成的 SQL(PostgreSQL 示例)
    SELECT "books"."id", "books"."title"
    FROM "books"
    WHERE "books"."title" LIKE %s;
    
    # 参数:['%Django%']

四、聚合查询的实现

1. annotate() 方法
  • 实现原理

    1. 在 SELECT 列表中添加聚合表达式
    2. 自动添加 GROUP BY 子句(如果需要)
  • 示例

    python 复制代码
    # 查询每本书的评论数
    books = Book.objects.annotate(review_count=Count("reviews"))
    
    # 生成的 SQL(简化)
    SELECT 
        "books"."id", 
        "books"."title", 
        COUNT("reviews"."id") AS "review_count"
    FROM "books"
    LEFT OUTER JOIN "reviews" ON ("books"."id" = "reviews"."book_id")
    GROUP BY "books"."id";
2. F() 表达式与数据库计算
  • 作用:直接在 SQL 中引用字段值,避免 Python 层面的数据传输

  • 示例

    python 复制代码
    # 将所有书的价格提高 10%
    Book.objects.update(price=F("price") * 1.1)
    
    # 生成的 SQL
    UPDATE "books" SET "price" = "books"."price" * 1.1;

五、优化机制

1. 查询缓存
  • 一级缓存QuerySet._result_cache

    • 首次求值后缓存结果
    • 重复访问直接返回缓存
  • 示例

    python 复制代码
    qs = Book.objects.all()
    list(qs)  # 执行 SQL 并缓存结果
    list(qs)  # 直接从缓存获取,不执行 SQL
2. 延迟加载与预加载
  • 延迟加载(Lazy Loading)

    python 复制代码
    book = Book.objects.first()
    reviews = book.reviews.all()  # 触发额外 SQL 查询
  • 预加载(Eager Loading)

    python 复制代码
    # 使用 prefetch_related() 减少查询次数
    books = Book.objects.prefetch_related("reviews")
    for book in books:
        reviews = book.reviews.all()  # 无需额外查询

六、源码分析示例

1. QuerySet.filter() 方法
python 复制代码
# django/db/models/query.py
class QuerySet:
    def filter(self, *args, **kwargs):
        return self._filter_or_exclude(False, *args, **kwargs)
        
    def _filter_or_exclude(self, negate, *args, **kwargs):
        clone = self._chain()  # 创建 QuerySet 副本
        clone.query.add_q(Q(*args, **kwargs))  # 添加查询条件到 query 对象
        if negate:
            clone.query.negate()
        return clone
2. SQLCompiler.as_sql() 方法
python 复制代码
# django/db/backends/sqlite3/compiler.py
class SQLCompiler:
    def as_sql(self):
        # 生成 SELECT 列表
        select, select_params = self.get_select()
        
        # 生成 FROM 子句
        from_, from_params = self.get_from_clause()
        
        # 生成 WHERE 子句
        where, where_params = self.compile(self.query.where)
        
        # 生成 ORDER BY 子句
        order_by, order_params = self.get_order_by()
        
        # 组装完整 SQL
        sql = "SELECT %s FROM %s" % (select, from_)
        if where:
            sql += " WHERE %s" % where
        if order_by:
            sql += " ORDER BY %s" % order_by
            
        return sql, select_params + from_params + where_params + order_params

七、常见性能陷阱与优化

1. N+1 查询问题
  • 问题场景

    python 复制代码
    # 触发 1 次查询获取所有书
    books = Book.objects.all()
    
    # 遍历每本书时触发 N 次查询获取作者
    for book in books:
        print(book.author.name)  # 每次循环触发一次 SQL
  • 优化方案

    python 复制代码
    # 使用 select_related() 减少查询次数到 1 次
    books = Book.objects.select_related("author")
    for book in books:
        print(book.author.name)  # 无需额外查询
2. 过度使用 values()
  • 错误做法

    python 复制代码
    # 返回字典,失去模型方法
    data = Book.objects.values("id", "title")
  • 优化方案

    python 复制代码
    # 使用 only() 仅加载需要的字段,保留模型实例
    books = Book.objects.only("id", "title")
相关推荐
松涛和鸣2 小时前
72、IMX6ULL驱动实战:设备树(DTS/DTB)+ GPIO子系统+Platform总线
linux·服务器·arm开发·数据库·单片机
likangbinlxa2 小时前
【Oracle11g SQL详解】UPDATE 和 DELETE 操作的正确使用
数据库·sql
r i c k3 小时前
数据库系统学习笔记
数据库·笔记·学习
野犬寒鸦3 小时前
从零起步学习JVM || 第一章:类加载器与双亲委派机制模型详解
java·jvm·数据库·后端·学习
IvorySQL4 小时前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
·云扬·4 小时前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德4 小时前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫4 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i4 小时前
完全卸载MariaDB
数据库·mariadb
纤纡.4 小时前
Linux中SQL 从基础到进阶:五大分类详解与表结构操作(ALTER/DROP)全攻略
linux·数据库·sql