2015-09-06 106 views
1

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

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

和代表嵌套循环的查询的连接是这些:

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

正如你所看到的,最后两个查询是完全一样的。这是我所迷惑的。为什么不只是生成前两个查询还不够?为什么我们必须生成第三个?

+1

您是否尝试过使用'Hibernate JPA 3.6.0'创建查询?看起来像一个双拼复制/粘贴错字。 –

回答

1

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

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

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操作,它可能会加载一些额外的销售对象。但通常这种启发式会带来更好的性能,因为它可以大大减少查询次数。通常加载一些额外的数据不是问题,减少到服务器的往返次数更重要。