Kos*_*zak 0 nhibernate criteria subquery icriteria queryover
我在谷歌和stackoverflow上搜索类似的问题将近2个小时,但没有找到任何解决方案.
我有2个表,关系1对多.
1) [Accounts]
PK Account_Id
int User_ID
2) [Temporary_Accounts]
Fk Account_Id
char IsAccepted {'1','0',null}
varchar name
Run Code Online (Sandbox Code Playgroud)
和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;
Run Code Online (Sandbox Code Playgroud)
我想显示给定user_id的所有帐户以及在[Temporary_Accounts]和IsAccepted == null中有记录的Accounts的附加信息(fe name).
所以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);
Run Code Online (Sandbox Code Playgroud)
但我的IQueryOverquery:
IQueryOver<Acc> query = (...)
query.JoinAlias(f => f.Temp,
() => Temp,
JoinType.LeftOuterJoin)
.Where(f => f.Temp.IsAccepted == null)
.And(f => f.user.id == userid);
Run Code Online (Sandbox Code Playgroud)
生成这样的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);
Run Code Online (Sandbox Code Playgroud)
所以我得到的结果比第一次正确的查询要少.
你有什么想法我应该改变什么,或者我可以做些什么来获得第一次查询的结果?我的想法是使用子查询leftjoin Accounts表,它从Temporary_Accounts表中选择所有IsAccepted = null帐户,但我不知道如何在Iqueryover或Icriteria中执行此操作.
我会很感激任何建议
既然你有1-Many之间Acc和Temp你的样本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);
Run Code Online (Sandbox Code Playgroud)
像这样生成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
)
Run Code Online (Sandbox Code Playgroud)
关于nhibernate.info的这篇文章对于使用QueryOver计算复杂查询非常有帮助.
更新:
如果你还需要查找Accounts哪些没有任何相应的行,Temporary_Accounts那么你需要两个子查询和一个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))
);
Run Code Online (Sandbox Code Playgroud)
更新2:
从NH 3.2开始,您可以为JOIN添加额外条件.有关更多详细信息,请参阅此答案:使用NHibernate ICriteria/QueryOver查询向外部联接添加条件
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>();
Run Code Online (Sandbox Code Playgroud)