2015-09-06 69 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操作,它可能會加載一些額外的銷售對象。但通常這種啓發式會帶來更好的性能,因爲它可以大大減少查詢次數。通常加載一些額外的數據不是問題,減少到服務器的往返次數更重要。