了解ORM的嵌套选择

时间:2015-09-06 05:14:27

标签: sql join orm sql-subselect

我正在阅读the article解释嵌套循环连接算法,但我并不完全理解嵌套选择的实际工作原理。以下是文章提供的示例:

  

示例搜索姓氏以“WIN”开头的员工   并为这些员工提取所有销售额。

表示嵌套循环连接的查询是:

select employees0_.subsidiary_id as subsidiary1_0_
       -- MORE COLUMNS
from employees employees0_ 
where upper(employees0_.last_name) like ?;

select sales0_.subsidiary_id as subsidiary4_0_1_
         -- MORE COLUMNS
from sales sales0_
where sales0_.subsidiary_id=? 
  and sales0_.employee_id=?;

select sales0_.subsidiary_id as subsidiary4_0_1_
         -- MORE COLUMNS
from sales sales0_
where sales0_.subsidiary_id=? 
  and sales0_.employee_id=?;

如您所见,最后两个查询完全相同。这就是我所困惑的。为什么不只是生成前两个查询不够?为什么我们必须生成第三个?

2 个答案:

答案 0 :(得分:1)

请记住,您粘贴的代码是要做的参考文章示例 - 反模式。

也就是说,查询是参数化的,因此实际上并不相同。每个查询中的两个初始?字符在for循环的每次迭代中都会被subsidiary_id的不同值替换。

答案 1 :(得分:0)

没有必要生成第三个查询。如果手动编写SQL查询,则可以将所有检索到的员工的所有销售作为单个查询加载。但是当程序代码在文章中看起来像是“N + 1查询”反模式时出现:

for (Employees e: emp) {
  // process Employee
  for (Sales s: e.getSales()) {
    // process sale for Employee
  }
}

在该代码中,e.getSales()方法为一名员工加载数据。此方法也没有足够的信息来加载所有其他员工的销售数据,因为ORM没有必要加载销售数据的完整员工列表。因此,ORM被迫在单独的查询中加载每个员工的销售数据。

某些ORM可以自动避免“N + 1查询”问题。例如,在PonyORM(用Python编写)中,文章中的代码将如下所示:

# the first query loads all necessary employees
employees = select(e for e in Employee if e.lastName.startswith('WIN'))

for e in employees:
    # process Employee
    for sale in e.sales:
        # process sale for Employee

当程序开始循环遍历员工查询时,PonyORM会立即加载所有必要的员工。当请求第一个员工的销售项目时,PonyORM仅为该员工加载它(因为ORM不知道我们的意图并且假设我们可能只需要第一个员工的销售数据)。但是,当请求第二个员工的销售数据时,PonyORM会注意到“N + 1查询”反模式,看到我们在内存中加载了N个员工对象,并在单个查询中加载了所有剩余员工的销售额。此行为可以视为启发式。如果我们的for - 循环包含break操作,它可能会加载一些额外的销售对象。但通常这种启发式方法可以提高性能,因为它可以大大减少查询次数。通常,加载一些额外数据不是问题,减少到服务器的往返次数就更为重要。