2016-12-20 1 views
6

Рассмотрим следующие две таблицы:Каковы альтернативы использованию ORDER BY в подзапросе в API-интерфейсе JPA?

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

Где Status содержит все статусы, в которых Project было.

Допустим, мы хотим запросить все проекты, для которых последний статус имеет имя «новый». Запрос Sql, что я придумал это:

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) 

Я пытаюсь повторить вышеуказанный запрос с использованием Criteria API, и я заметил, что класс CriteriaQuery имеет метод orderBy но класс Subquery не делает.

критериям запроса, что я придумал до сих пор:

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)) 

я застрял здесь, потому что Subquery sq не имеет какой-либо метод для сортировки его результатов и возвращает только последнюю.

Каковы альтернативы сортировке подзапроса, чтобы получить желаемый результат в описанном выше сценарии?

+0

строки на основе JPQL не позволяет ORDER BY в подзапросе, а критерии просто отражают это. "subquery :: = simple_select_clause subquery_from_clause [where_clause] [groupby_clause] [having_clause]" –

+0

@NeilStockton Я понял это, поэтому я прошу об альтернативных решениях –

+0

, чтобы вы хотели вернуть наивысшее значение «sq.id» из подзапрос (с учетом предложения where)? не можете ли вы просто выполнить подзапрос «SELECT MAX (sq.id) FROM status sq WHERE q.id_project = sq.id_project»? –

ответ

2

подзапрос может быть написана с использованием SELECT MAX вместо 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) 

Это также будет быстрее, заказав все записи является медленным по сравнению на поиск максимального значения. Как уже было опубликовано, его можно перевести в CriteriaQuery.

К сожалению Критерии запросы не поддерживают порядок в подчиненном запросе - увидеть эти связанные ответы:

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

Альтернатива, если вы действительно нужен ORDER BY (например,чтобы диапазон значений в примере ниже), чтобы использовать нативный запрос вместо CriteriaQuery:

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

Ваш подзапрос, как вы заявили, это, было бы равносильно JPQL из

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

, которые должны быть полностью поддерживаются спецификации JPA. с точки зрения критериев API, я предположил бы, что в этом

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

В дополнение к другим ответам, я не думаю, что оптимизатор базы данных может неявно переписать коррелированный подзапрос с агрегатными функциями в нескоррелированую форму, поэтому я думаю, что писать некоррелированный подзапрос явно лучше производительность мудрой (ниже пример использует JPQL, критерии API эквивалент должен быть простым):

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) 
Смежные вопросы