2012-02-14 3 views
7

비슷한 질문을 보았지만 간단한 설명을 찾을 수 없습니다. 나는 그것을 놓칠 수 있었지만 나는 보았다고 약속한다. 실제로 나는 모든 것을 빠르게 채색하고 다른 형태의 NH에 익숙하다고 가정하는 단일 블로그 게시물 이외의 다른 문서도 찾을 수 없습니다.NHibernate QueryOver 하위 쿼리

감안할 때 대다, 나는 아마도 하위 주제를 포함하여 주어진 Topic에 대한 모든 Programs를 검색 할 후자는 Topics의 계층 구조에 ProgramTopic 사이. 주어진 부모 주제의 여러 하위 주제에 프로그램이 나열 될 수 있으므로 하위 쿼리를 사용하거나 별개의 것을 사용해야하는 경우를 고려해야합니다 (간단한 접근 방식 인 TransformUsing(Transformers.DistinctRootEntity)이 작동하지 않음). 결과는 뷰에 전송 모델 유형에 던져 무언가

SELECT ProgramId, Title, bar, baz, foo FROM Programs 
WHERE ProgramId IN 
(SELECT ProgramId from Program_Topics WHERE TopicId IN (1, 2, ...)) 

처럼

원시 SQL이 있어야한다. 내 최초의 시도가이 있었다 :

ProgramDTO pDTO = null; 

/* topicIds is List<int> passed into function */ 

var query = Session.QueryOver<Program>() 
.JoinQueryOver<Topic>(p => p.Topics) 
.WhereRestrictionOn(pt => pt.Id).IsInG<int>(topicIds)  
.TransformUsing(Transformers.DistinctRootEntity) 
.SelectList(list => list 
     .Select(program => program.Id).WithAlias(() => pDTO.Id) 
     .Select(program => program.Title).WithAlias(() => pDTO.Title) 
     .Select(program => program.Location).WithAlias(() => pDTO.Location) 
     .Select(program => program.Description).WithAlias(() => pDTO.Description) 
) 
.TransformUsing(Transformers.AliasToBean(typeof(ProgramDTO))); 

return query.List<ProgramDTO>();  

는 분명히 이것은 대신 하위 쿼리의 조인 실행,하지만 난에 하위 쿼리를 수행하는 예를 찾을 수없는 대다이있다.

public class Program : Entity { 
    public virtual ISet<Topic> Topics { get; protected internal set; } 
    ... 
} 

public class Topic : Entity { 
    public virtual ISet<Program> Programs { get; protected internal set; } 
    public virtual Topic ParentTopic { get; protected internal set; } 
    ... 
} 

답변

4

음,이에 해시 좀 더, 나는 결과의 한 부분을 좋아하지 않는 동안, 그렇습니다 일 :

var distinctProgIdsSubQuery = QueryOver.Of<Program>(). 
JoinQueryOver<Topic>(p => p.Topics). 
WhereRestrictionOn(pt => pt.Id).IsIn(topicIds) 
.Select(Projections.Distinct(Projections.Property<Program>(p => p.Id))); 


ProgramDTO pDTO = null; 
var progQuery = Session.QueryOver<Program>() 
    .WithSubquery.WhereProperty(p => p.Id).In(distinctProgIdsSubQuery) 
    .SelectList(list => list 
     .Select(program => program.Id).WithAlias(() => pDTO.Id) 
     .Select(...) 
     ) 
    .TransformUsing(Transformers.AliasToBean(typeof(ProgramDTO))); 


return progQuery.List<ProgramDTO>(); 

SELECT this_.ProgramId as y0_, ... 
FROM Programs this_ 
WHERE this_.ProgramId in (
     SELECT distinct this_0_.ProgramId as y0_ 
     FROM 
      Programs this_0_ 
     inner join 
      Programs_Topics topics3_ 
       on this_0_.ProgramId=topics3_.ProgramId 
     inner join 
      Topics topic1_ 
       on topics3_.TopicId=topic1_.TopicId 
     WHERE 
      topic1_.TopicId in (
       @p1, @p2, ... 
      ) 
    ) 

이이 제한 될 수 생산 NH의,하지만 서브 프로그램에 테이블에 가입해야합니다. 나는 이것을 다른 방향, 즉 QueryOver.Of<Topic>()을 만들려고했으나 끝에서 프로그램 ID를 선택하는 방법을 알지 못했다. select는 나에게 TopicIds만을주고 있었고 심지어 쿼리도 여전히 세 테이블 모두를 합칩니다.

MS-SQL의 쿼리 최적화 프로그램이 쓸모없는 조인을 피할 수 있을지 모르겠지만, 우리가 의존 할 필요가 없다면 좋을 것입니다.

지금까지는이 방법이 효과적이었습니다. 다른 사람이 두려움을 덜어주기를 바랍니다.

+1

이것은 실제로 당신이 대답이 아닙니다. 당신이 피하고 싶었던 조인을 생성합니다. 문제가 쉽게 해결되는 대신 손으로 직접 작성한 HQL 쿼리를 사용하기로했습니다. – Oliver

+1

HQL 쿼리를 직접 작성하면 강력한 타이핑 및 리팩토링을 포기하므로 ​​QueryOver의 주요 이점입니다. 추가 JOIN을 생성하는 동안 SQL 쿼리 최적화 프로그램은 JOIN 절을 읽고 대신 해당 필드를 선택하여 동일한 결과를 얻을 수있는 추가 테이블이 필요 없음을 알게 될 것으로 기대합니다. 그 밖의 것이 없다면 먼저 WHERE를 실행 한 다음 인덱스에 조인 할 것이므로 빠를 것입니다. –

+0

SQL Server에서 SQL 코드를 실행할 수 있다는 가능성과 가능한 최적화 가능성에 대한 지식이 부족하므로 너무 많이 의존하지 않는 것이 좋습니다. 하지만 통찰력을 주셔서 감사합니다 :-) – Oliver

10

ID가 포함 된 분리 된 쿼리를 만든 다음이 하위 쿼리를 기본 쿼리와 함께 사용해야합니다. 당신이 등을 클래스 이름으로 해당 비트를 교체해야합니다, 그래서 내가 여기에 예를 붙여 넣은

먼저 설정 (이 비트 무시할 수) : -

public class TestDto { 
    public long Id { get; set; } 
    public string Name { get; set; } 
} 
... 
TestDto dto = null; 
var ids = new List<int> { 1,2,5,7 }; 

지금 dettached 쿼리 -

var idSubQuery = QueryOver.Of<CmsRegionContent>() 
    .WhereRestrictionOn(w => w.Id).IsIn(ids) 
    .Select(Projections.Distinct(Projections.Property<CmsPage>(s => s.Id))); 

그리고 마지막 비트가 모두 함께 넣어하는 것입니다 : -

이다음과 같은 SQL을 생성합니다 : -

SELECT 
    this_.Id as y0_, 
    this_.PageName as y1_ 
FROM cmspage this_ inner join cmsregioncontent cmsregionc1_ 
    on this_.Id=cmsregionc1_.PageId 
WHERE cmsregionc1_.Id in (
    SELECT 
     distinct this_0_.Id as y0_ 
    FROM cmsregioncontent this_0_ 
    WHERE this_0_.Id in (
     1 /* ?p0 */, 
     2 /* ?p1 */, 
     5 /* ?p2 */, 
     7 /* ?p3 */) 
    ) 

바라건대 당신은 당신의 클래스/속성 이름이 따라 할 수있을 것입니다.

+0

이 문제를 해결할 수 있었지만 여전히 관계 테이블에 조인되므로 기본 엔터티 당 여러 행이 반환됩니다. 'SELECT * FROM cmspage WHERE id IN (SELECT ... cmsPageId FROM cmsregioncontent WHERE regionid IN (...))'과 비교하십시오. 여기에 주어진 버전은 Inner Join을 수행하는 것만 큼 크게 다르지 않습니다. –

+1

사실 확인 후 NH은 여전히 ​​프로그램 INNER JOIN 프로그램 토픽 INNER JOIN 주제를 생성하고 있습니다. 'var topicSubQuery = QueryOver.Of () .WhereRestrictionOn (pt => pt.Id) .IsIn (topicIds) .Select (Projections.Distinct (Projections.Property (p => p.Id)));' 'var progQuery = Session.QueryOver () .Where (p => p.Status! = ProgramStatus.Archived) .JoinQueryOver (p => p.Topics) .WithSubquery.WhereProperty (pt => pt.Id). (topicSubQuery) .SelectList (list => list/* ... snip ... * /) \t \t \t.TransformUsing (Transformers.AliasToBean (typeof (ProgramDTO))); ' –

관련 문제