在 Odoo 中,确实可以通过 SQL 语句来提升一些功能逻辑的处理效率。将 SQL 转为 Python 代码,并在 Odoo 模块中使用,可以通过以下几种方式实现。

感悟人生263 / 2025-02-21 / 原文

1. 使用 env.cr.execute 执行 SQL 语句

Odoo ORM 提供的 env.cr.execute() 可以直接执行 SQL 语句,这样可以在 Python 代码中调用 SQL 逻辑,结合 Odoo 的业务模型实现复杂的逻辑操作。execute() 方法适合处理批量数据更新、复杂查询等。

示例:批量更新 customer_id 字段

def update_customer_ids(self):
    query = """
        UPDATE purchase_account_invoice AS pai
        SET customer_id = COALESCE(
            (SELECT delivery_order.customer_id FROM delivery_order WHERE delivery_order.id = pai.delivery_order_id),
            (SELECT purchase_order.customer_id FROM purchase_order WHERE purchase_order.id = pai.purchase_order)
        )
        WHERE pai.delivery_order_id IS NOT NULL OR pai.purchase_order IS NOT NULL;
    """
    self.env.cr.execute(query)
    self.env.cr.commit()  # 提交更改

注意:SQL 操作后,可以用 invalidate_cache() 来刷新缓存,确保 ORM 缓存数据一致

 

2. 使用 SQL 函数实现数据聚合或计算

在 Odoo 中处理数据聚合、统计和复杂计算时,可以用 SQL 函数(如 SUMCOUNTAVG 等)直接在数据库中完成计算,减少了 Python 循环操作,能大幅提高效率。

示例:统计每位客户的总订单金额

def get_total_order_amount_by_customer(self):
    query = """
        SELECT partner_id, SUM(amount_total)
        FROM sale_order
        GROUP BY partner_id
    """
    self.env.cr.execute(query)
    results = self.env.cr.fetchall()
    # 处理查询结果
    for partner_id, total_amount in results:
        print(f"客户ID: {partner_id}, 总订单金额: {total_amount}")

 

 

3. 使用 env.cr.fetchall() 获取 SQL 查询结果

fetchall() 方法可以将 SQL 查询结果作为列表返回,适用于需要将查询结果进一步处理的情况。对于复杂的查询结构,fetchall() 提供了一个非常方便的方式将数据转化为 Python 可用的格式。

示例:筛选满足特定条件的订单

def get_filtered_orders(self):
    query = """
        SELECT id, name, amount_total
        FROM sale_order
        WHERE state = 'sale' AND amount_total > 5000
    """
    self.env.cr.execute(query)
    results = self.env.cr.fetchall()
    return [{'id': row[0], 'name': row[1], 'amount_total': row[2]} for row in results]

 

 

4. 使用 WITH 语句和 SQL CTE(公用表表达式)

对于需要多层嵌套查询的复杂 SQL 逻辑,可以使用 WITH 语句创建临时表,然后在 Python 代码中使用 env.cr.execute() 来执行。CTE 非常适合处理复杂的条件筛选、分组和聚合查询。

示例:计算每月的销售总额

def get_monthly_sales(self):
    query = """
        WITH monthly_sales AS (
            SELECT DATE_TRUNC('month', order_date) AS month, SUM(amount_total) AS total
            FROM sale_order
            WHERE state = 'sale'
            GROUP BY DATE_TRUNC('month', order_date)
        )
        SELECT month, total FROM monthly_sales ORDER BY month;
    """
    self.env.cr.execute(query)
    results = self.env.cr.fetchall()
    return [{'month': row[0], 'total': row[1]} for row in results]

 

 

5. 使用 Odoo ORM 结合 SQL 优化批量操作

在某些情况下,将 Odoo ORM 的便利性和 SQL 的高效性结合使用,可以优化批量操作。比如在进行多条数据筛选和更新时,可以用 ORM 的 search 查找到所需的记录,然后用 SQL 批量更新,既保持了 ORM 的兼容性,也享受了 SQL 的效率。

示例:更新符合条件的记录,避免逐条写入

def update_large_records(self):
    record_ids = self.env['purchase.account.invoice'].search([]).ids
    if record_ids:
        query = """
            UPDATE purchase_account_invoice
            SET customer_id = 1  -- 假设所有满足条件的记录的 customer_id 更新为 1
            WHERE id = ANY(%s)
        """
        self.env.cr.execute(query, (record_ids,))
        self.env.cr.commit()

 

6. 在 @api.model@api.multi 方法中结合 SQL 执行业务逻辑

在自定义模型方法中,可以使用 SQL 来执行业务逻辑,并返回结构化数据,进一步结合 ORM 实现功能。

示例:在自定义按钮操作中批量执行 SQL 操作

from odoo import models, api

class PurchaseAccountInvoice(models.Model):
    _name = 'purchase.account.invoice'

    @api.multi
    def action_bulk_update(self):
        for record in self:
            query = """
                UPDATE purchase_account_invoice
                SET customer_id = %s
                WHERE id = %s
            """
            self.env.cr.execute(query, (record.new_customer_id, record.id))
        self.env.cr.commit()

 

 

总结

  • env.cr.execute():在 Odoo 中执行 SQL 语句的核心方法,适用于批量操作和复杂查询。
  • 结合 SQL 函数:通过 SQL 中的聚合函数如 SUMCOUNT 等,实现高效的数据统计。
  • fetchall() 返回结果:配合 execute() 使用,将查询结果处理为 Python 的数据格式。
  • WITH 语句和 CTE:用于多层嵌套查询和复杂的 SQL 逻辑处理。
  • SQL 和 ORM 结合:对于批量数据更新,将 Odoo ORM 的便利性和 SQL 的高效性结合,可以获得最佳性能。

这种结合 SQL 与 Python 代码的方式,不仅能提升代码效率,还能简化业务逻辑。