1

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

这些示例搜索姓氏以“WIN”开头的员工,并获取这些员工的所有 SALES。

代表嵌套循环连接的查询如下:

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=?;

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

4

2 回答 2

1

请记住,您粘贴的代码是参考的文章示例,说明该做什么——一种反模式。

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

于 2015-09-06T15:27:11.250 回答
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-loop 包含,它可能会加载一些额外的销售对象break手术。但通常这种启发式会带来更好的性能,因为它可以大大减少查询的数量。通常加载一些额外的数据不是问题,减少到服务器的往返次数更为重要。

于 2015-09-07T11:56:28.030 回答