2016-12-20 15 views
6

Rozważmy następujące dwie tabele:Jakie są alternatywy dla użycia ORDER BY w Podzapytanie w API JPA Criteria?

  1. Project (id, project_name)
  2. Status (id, id_project, status_name)

Gdzie Status zawiera wszystkie stany, w których Project wynikły.

Powiedzmy, że chcemy zapytać o wszystkie projekty, dla których najnowszy status ma nazwę "nowy". Zapytania SQL, które wymyślić to:

SELECT q.id_project FROM status q 
WHERE q.status_name like 'new' 
AND q.id IN (
    SELECT TOP 1 sq.id from status sq 
    WHERE q.id_project = sq.id_project 
    ORDER BY sq.id DESC) 

Próbuję replikować powyższej kwerendy przy użyciu Criteria API i zauważyłem, że klasa CriteriaQuery ma metodę orderBy ale klasa Subquery nie.

Query Kryteria że mam wymyślić tak daleko jest:

CriteriaQuery<Project> q = criteriaBuilder.createQuery(Project.class); 
Root<Status> qFrom  = q.from(Status.class); 
Subquery<Integer> sq  = q.subquery(Integer.class); 
Root<Status> sqFrom  = sq.from(Status.class); 

sq.select(sqFrom.get(Status_.id)) 
    .where(criteriaBuilder.equal(sqFrom.get(Status_.project), qFrom.get(Status_.project)) 

utknąłem tutaj, ponieważ Subquery sq nie ma żadnego sposobu sortowania swoje wyniki i powrocie tylko ostatnią jeden.

Jakie są alternatywy dla sortowania podzapytania w celu uzyskania pożądanego rezultatu w opisanym powyżej scenariuszu?

+0

String oparte JPQL nie pozwala ORDER BY w podzapytaniu, a kryteria po prostu odzwierciedlają to. "subquery :: = simple_select_clause subquery_from_clause [where_clause] [groupby_clause] [have_clause]" –

+0

@NeilStockton Zrozumiałem to, dlatego proszę o alternatywne rozwiązania –

+0

, więc chcesz zwrócić najwyższą wartość "sq.id" z podzapytanie (z zastrzeżeniem klauzuli where)? nie możesz po prostu wykonać podkwerendy "SELECT MAX (sq.id) FROM status sq WHERE q.id_project = sq.id_project"? –

Odpowiedz

2

Podzapytanie mogłyby być napisane przy użyciu SELECT MAX zamiast SELECT TOP 1 ... ORDER BY ...:

SELECT q.id_project FROM status q 
WHERE q.status_name like 'new' 
AND q.id = (
    SELECT MAX(sq.id) from status sq 
    WHERE q.id_project = sq.id_project) 

Byłoby to również szybciej jak zamawianie wszystkie rekordy jest powolny w porównaniu do znalezienia maksymalnej wartości. Jak już opublikowano, można go przetłumaczyć na CriteriaQuery.

Niestety Zapytania kryteria nie obsługiwać zamawiania w podkwerendzie - zobacz te związane odpowiedzi:

https://stackoverflow.com/questions/19549616#28233425 https://stackoverflow.com/questions/5551459#5551571

Alternatywą jeśli naprawdę potrzebują ORDER BY (npaby umożliwić szeroki zakres wartości w poniższym przykładzie) jest użycie rodzimych kwerendy zamiast CriteriaQuery:

Query query = entityManager.createNativeQuery(
    "SELECT bar FROM foo WHERE bar IN (SELECT TOP 10 baz FROM quux ORDER BY quuux)"); 
2

Twój podzapytanie, jak stwierdzili to, by zrównać się z JPQL

SELECT MAX(sq.id) FROM status sq WHERE q.id_project = sq.id_project 

które powinny być w pełni obsługiwany przez spec WZP. pod względem kryteriów API, to myślę, że w tym

Subquery<Integer> sq  = q.subquery(Integer.class); 
Root<Status> sqFrom  = sq.from(Status.class); 
Expression maxExpr = criteriaBuilder.max(sqFrom.get(Status_.id)); 
sq.select(maxExpr).where(criteriaBuilder.equal(sqFrom.get(Status_.project), qFrom.get(Status_.project)) 
1

oprócz innych odpowiedzi, nie sądzę, że optymalizator baza danych jest w stanie w sposób dorozumiany przepisać skorelowane podzapytanie z kruszywa funkcji do nieskorelowane formie, więc myślę, że pisanie skorelowane podzapytanie wyraźnie jest lepsza wydajność mądry (poniżej przykład używa JPQL, Kryteria odpowiednik API powinny być proste):

select q.project from Status q 
where q.name = 'new' 
    and q.id in (select max(sq.id) from Status sq group by sq.project.id) 
Powiązane problemy