2012-10-03 14 views
6

Hallo Stackoverflow Benutzer,NHibernate - Union drei QueryOvers

ich in dieses Problem lief Ich habe drei QueryOvers und jeder von ihnen gibt eine Liste von Kandidaten-IDs, die ich dann diese Kandidaten bringen verwenden. Dafür habe ich den folgenden Code geschrieben.

 private IQueryOver<CandidateEntity, CandidateEntity> UnionPublicWithPrivateCandidates(
     IQueryOver<CandidateEntity, CandidateEntity> publicCandidates, 
     IQueryOver<CandidateEntity, CandidateEntity> privateCandidate, 
     IQueryOver<CandidateEntity, CandidateEntity> candidatesByUserRole) 
    { 
     return ActiveCandidatesQueryOver.Where(Restrictions.Disjunction() 
             .Add(Subqueries 
              .WhereProperty<CandidateEntity>(c => c.Id) 
              .In((QueryOver<CandidateEntity>)publicCandidates.Select(c => c.Id))) 
             .Add(Subqueries 
              .WhereProperty<CandidateEntity>(c => c.Id) 
              .In((QueryOver<CandidateEntity>)privateCandidate.Select(c => c.Id))) 
             .Add(Subqueries 
              .WhereProperty<CandidateEntity>(c => c.Id) 
              .In((QueryOver<CandidateEntity>)candidatesByUserRole.Select(c => c.Id)))); 
    } 

Dies gibt die korrekten Ergebnisse und die erzeugte Abfrage sieht wie folgt aus

SELECT * 
FROM Applicants 
WHERE IsActive = 1 
    and (Id in (SELECT Id from **FirstQueryOver**) 
     **or** Id in (SELECT Id from **SecondQueryOver**) 
     **or** Id in (SELECT Id from **ThirdQueryOver**)) 

Das Problem ist, dass es verwendet ‚oder‘. Aus diesem Grund ist die Abfrage sehr langsam.

Wenn stattdessen ich dies schreibe:

SELECT * 
FROM Applicants 
WHERE IsActive = 1 
    and (Id in (SELECT Id from **FirstQueryOver** 
        union SELECT Id from **SecondQueryOver** 
        union SELECT Id from **ThirdQueryOver**)) 

Es ist fast sofort beendet.

Haben Sie eine Idee, wie sollte ich den Code für eine bessere Leistung umgestalten?

Vielen Dank, Adrian.

+0

macht eine In-Memory-Union eine Option? –

+0

@Andrew Whitaker Ich habe genau das gemacht, aber ich mag es nicht wirklich, weil es Datensätze gibt, die von mehr als einer Abfrage zurückgegeben werden, so dass ich die Duplikate manuell eliminieren muss. –

Antwort

1

ich gesucht, aber nichts konnte nicht gefunden werden, so habe ich den folgenden Hack:

private IQueryOver<CandidateEntity, CandidateEntity> UnionPublicWithPrivateCandidates(
        IQueryOver<CandidateEntity, CandidateEntity> publicCandidates, 
        IQueryOver<CandidateEntity, CandidateEntity> privateCandidate, 
        IQueryOver<CandidateEntity, CandidateEntity> candidatesByUserRole) 
{ 
    var excludedQueryCandidates = QueryOver 
     .WithSubquery.WhereNotExists(((QueryOver<CandidateEntity>)publicCandidates.Select(x => x.Id))) 
     .WithSubquery.WhereNotExists((QueryOver<CandidateEntity>)privateCandidate.Select(x => x.Id)) 
     .WithSubquery.WhereNotExists((QueryOver<CandidateEntity>)candidatesByUserRole.Select(x => x.Id)); 

    return QueryOver.WithSubquery.WhereNotExists((QueryOver<CandidateEntity>) excludedQueryCandidates.Select(Projections.Distinct(Projections.Id()))); 
} 

Es ist nicht die beste Lösung, sollte aber funktionieren.

0

Ich kann nicht sagen, warum das oder vs die Union einen solchen Unterschied hat. Der SQL-Profiler, der Abfrageanalysator und der geschätzte Ausführungsplan sollten Ihnen mehr Aufschluss darüber geben, was SQL für die Ausführung Ihrer Abfrage bedeutet. Ich nehme an, dass im ersten Fall nicht der richtige Index verwendet wird. Allerdings würde ich versuchen, Ihre Anfrage umzuschreiben:

SELECT * 
    FROM Applicants 
    WHERE IsActive = 1 
    and (this_.Id in (SELECT this_0_.Id from **FirstQueryOver** 
              or **SecondQueryOver** 
              or **ThirdQueryOver**)) 

und sehen, welche Vorform Sie dann haben.

+0

Hallo Peer, danke für deine Hilfe. Ich werde die Indizes überprüfen, die ich in meinen Spalten habe. Allerdings würde ich sehr schätzen, wenn Sie mir Hinweise in Refactoring der C# -Code geben könnte. –