2011-12-08 36 views
0

我正在尋找類似的問題谷歌和stackoverflow近2小時,但沒有找到任何解決方案。Nhibernate條件加入IQueryOver

我有2個關係1到很多表。

1) [Accounts] 
PK Account_Id 
int User_ID 


2) [Temporary_Accounts] 
Fk Account_Id 
char IsAccepted {'1','0',null} 
varchar name 

和2映射類

1) Acc 
int Id; 
User user; 
TempAcc Temp; //cause each Account can have 0 or one TempAcc (with IsAccepted == null) 

2)TempAcc 
int Id; 
bool IsAccepted; 
string name; 

我想爲與賬戶擁有在[Temporary_Accounts]記錄和IsAccepted == NULL的附加信息(f.e名)給出的user_id顯示所有帳戶。

所以SQL應該像:

select acc.Account_Id, acc.User_Id, tempacc.Name 

from Account acc left join Temporary_Account tempacc 
on (acc.Account_ID = tempacc.Account_Id and tempacc.IsAccepted is null) 
where (acc.User_Id = 65); 

但我IQueryOverquery:

IQueryOver<Acc> query = (...) 
query.JoinAlias(f => f.Temp, 
       () => Temp, 
       JoinType.LeftOuterJoin) 
    .Where(f => f.Temp.IsAccepted == null) 
    .And(f => f.user.id == userid); 

產生這樣的SQL:

select acc.Account_Id, acc.User_Id, tempacc.Name 

from Accounts acc left join Temporary_Accounts tempacc 
on (acc.Account_ID = tempacc.Account_Id) 
where (acc.User_Id = 65 and tempacc.IsAccepted is null); 

,所以我越來越少的結果比在第一個正確查詢。

你有什麼想法我應該改變什麼或者我能做些什麼來獲得第一個查詢的結果?我的想法是離開帳戶表與子查詢,它從Temporary_Accounts表中選擇所有IsAccepted = null帳戶,但我不知道如何在Iqueryover或Icriteria中執行此操作。

我會爲任何建議

回答

6

感激既然你有AccTemp您的樣品的SQL將生成一個笛卡兒產品1-多。

的Queryover你需要使用一個子查詢,看起來像下面這樣:

Acc accountAlias = null; 
var subQuery = QueryOver.Of<Temp>() 
       .Where(x=>x.IsAccepted==null) 
       .And(x=>x.Account.Id==accountAlias.Id); 

var results = session.QueryOver<Acc>(()=>accountAlias) 
       .Where(x=>x.User.Id==65) 
       .WithSubquery.WhereExists(subQuery); 

生產這樣的SQL:

select * 
from Accounts a 
where a.User_Id=65 
and exists (
    select t.Account_Id 
    from Temporary_Accounts t 
    where t.IsAccepted is null and t.Account_Id=a.Account_Id 
) 

本文就nhibernate.info是搞清楚複雜的查詢非常有幫助與QueryOver。

UPDATE:

如果您還需要找到Accounts不具有任何Temporary_Accounts相應行,那麼你需要兩個subqueries and a Disjunction

Acc accountAlias = null; 
var hasTempAccount = QueryOver.Of<Temp>() 
       .Where(x=>x.IsAccepted==null) 
       .And(x=>x.Account.Id==accountAlias.Id); 

var doesNotHaveTempAccount = QueryOver.Of<Temp>() 
       .And(x=>x.Account.Id==accountAlias.Id); 

var results = session.QueryOver<Acc>(()=>accountAlias) 
    .Where(x=>x.User.Id==65) 
    .Where(Restrictions.Disjunction() 
    .Add(Subqueries.WhereExists(hasTempAccount)) 
    .Add(Subqueries.WhereNotExists(doesNotHaveTempAccount))  
); 

更新2:

由於NH 3.2,你可以添加額外的條件,爲連接。看到這個答案的進一步細節:Adding conditions to outer joins with NHibernate ICriteria/QueryOver query

Temp tempAlias = null; 
Account accountAlias = null; 
dto dto = null; 
var results = Session.QueryOver<Account>(()=>accountAlias) 
    .JoinAlias(x=>x.TempAccounts,()=>tempAlias,JoinType.LeftOuterJoin, 
    Restrictions.IsNull(Projections.Property(()=>tempAlias.IsAccepted)) 
) 
    .Where(x=>x.Account.Id==65) 
    .SelectList(list=>list 
     .Select(()=>accountAlias.Id).WithAlias(()=>dto.AccountId) 
     .Select(()=>accountAlias.User.Id).WithAlias(()=>dto.UserId) 
     .Select(()=>tempAlias.Name).WithAlias(()=>dto.TempAccName) 
) 
    .SetResultTransformer(Transformers.AliasToBean<dto>()) 
    .List<dto>(); 
+0

謝謝馬克。我可能是錯的,但我認爲它會創建一些僞內連接,我需要左外連接。它只會返回具有臨時帳戶的帳戶,但不會在沒有帳戶的情況下返回帳戶(我也需要)。 – Kostrzak

+0

所以你想:「在TempAccounts中有一個條目的列表,其中IsAccepted爲null,並且所有在TempAccounts表中也沒有條目的帳戶」? –

+0

不幸的是,這並不容易。例如,如果user_id = 56有20個帳戶,我想顯示20個帳戶,但對於這些帳戶在temporary_accounts表中有isaccpted = null的記錄,我想要獲取有關temporary_account名稱的信息。簡單的左連接+在哪裏不工作原因爲1 Account_Id可以有a)臨時表中沒有記錄或b)1 /多條isAccepted ='1'的記錄c)1 /許多記錄isAccpted ='0'或d )0/1與isAccepted = null的記錄。我的問題中的第一個查詢確實是我想要的,但是如何將其轉發給IqueryOver? – Kostrzak