2011-12-08 20 views
0

googleとstackoverflowで同様の問題を約2時間探していましたが、解決策は見つかりませんでした。IqueryOverとの結合時のNhibernate条件

私は1対多の関係を持つ2つのテーブルを持っています。

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アカウントを選択するサブクエリを持つAccountsテーブルをleftjoinすることでしたが、IqueryoverまたはIcriteriaでそれを行う方法がわかりません。

あなたは1対多、あなたのサンプルSQLは、デカルト積を生成しますAcc間とTempを持っているので、私は

答えて

6

任意のアドバイスに感謝されます。

あなたが必要となりますQueryoverは、サブクエリを使用し、次のようになります。

このようなSQLを生産
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); 

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:

あなたもTemporary_Accounts内の任意の対応する行を持っていないAccountsを見つける必要がある場合、あなたは2 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))  
); 

UPDATE 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

あなたは次のようにします: "IsAcceptedがnullのTempAccountsのエントリを持つアカウントの一覧と、TempAccountsテーブルにもエントリを持たないアカウントの一覧"? –

+0

残念ながら、そう簡単ではありません。たとえば、user_id = 56のアカウントが20個ある場合、正確に20個のアカウントを表示したいのですが、temporary_accountsテーブルにisaccpted = nullのレコードがあるアカウントの場合、そのtemporary_accountの名前に関する情報を取得したいとします。シンプルな左結合+ 1つのAccount_Idの原因では動作しません。a)一時テーブルにレコードがありません。または、b)isAccepted = '1'を持つ1/)isAccepted = nullの0/1レコード。私の質問の最初の質問は、私が欲しいと思っていますが、IqueryOverにどのように伝えるか? – Kostrzak