Мы часто используем критерии в нашем проекте для динамической генерации запросов. Мне очень нравится, как выражаются запросы. Проблема в том, что мы обнаружили, что этот конкретный запрос не может быть выполнен с использованием индексов, основанных на clientId, и поэтому нам нужно его изменить. Вот рабочий неиндексированный запрос:
public List<EventInstance> getEventInstances(Date from, Date to, Integer clientId) {
Session session = this.getSession();
Criteria criteria = session.createCriteria(EventInstance.class);
if(clientId != null){
criteria.add(Restrictions.disjunction()
.add(Restrictions.eq("clientId", clientId))
.add(Restrictions.eq("team1ClientId", clientId))
.add(Restrictions.eq("team2ClientId", clientId))
);
}
if(from != null){
criteria.add(Restrictions.ge("startTime", from));
}
if(to != null){
criteria.add(Restrictions.le("startTime", to));
}
@SuppressWarnings("unchecked")
List<EventInstance> events = criteria.list();
this.releaseSession(session);
return events;
}
Этот запрос может использовать только индекс startTime и не может использовать индексы с любым из идентификаторов клиента. Я обнаружил, что следующая форма запроса эффективно использует наши индексы, и я хочу создать этот запрос с критериями:
select * from ( select * from eventInstance where (clientId = 8 or team1ClientId = 8 or team2ClientId = 8) ) evtalias where evtalias.startTime < now()
Мне удалось сделать дополнительный выбор в предложении WHERE с этим кодом:
public List<EventInstance> getEventInstances(Date from, Date to, Integer clientId){
Session session = this.getSession();
DetachedCriteria subSelectClient = DetachedCriteria.forClass(EventInstance.class);
if(clientId != null){
subSelectClient.add(Restrictions.disjunction()
.add(Restrictions.eq("clientId", clientId))
.add(Restrictions.eq("team1ClientId", clientId))
.add(Restrictions.eq("team2ClientId", clientId))
)
.setProjection(Property.forName("id"));
}
Criteria criteria = session.createCriteria(EventInstance.class);
if(clientId != null){
criteria.add(Property.forName("id").in(subSelectClient));
}
if(from != null){
criteria.add(Restrictions.ge("startTime", from));
}
if(to != null){
criteria.add(Restrictions.le("startTime", to));
}
@SuppressWarnings("unchecked")
List<EventInstance> events = criteria.list();
this.releaseSession(session);
return events;
}
Это создает запрос, подобный этому:
select * from eventInstance this_ where this_.id in (select this_.id as y0_ from eventInstance this_ where (this_.clientId=8 or this_.team1ClientId=8 or this_.team2ClientId=8)) and this_.startTime<=now();
Что еще хуже при использовании индексов, чем мой исходный запрос, и не выбирается в FROM.
Итак, мой вопрос, могу ли я сделать это по критериям, или я застрял с HQL или даже с собственным SQL. В качестве альтернативы, если вы знаете, как создать индекс, который будет работать, чтобы решить мою проблему, но я понимаю из документации mysql, что это невозможно.
Вот вывод из объяснения для целевого запроса, который я хочу создать:
mysql> explain select * from ( select * from eventInstance where (clientId = 8 or team1ClientId = 8 or team2ClientId = 8) ) evtalias where evtalias.startTime < now();
+----+-------------+---------------+-------------+-------------------------------+----- ------------------+---------+------+------+------------------------------------------------ --------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------+-------------+-------------------------------+-----------------------+---------+------+------+--------------------------------------------------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 288 | Using where |
| 2 | DERIVED | eventInstance | index_merge | eijoin2,ei_client,team2,team1 | ei_client,team1,team2 | 5,5,5 | NULL | 300 | Using union(ei_client,team1,team2); Using where; Using index |
+----+-------------+---------------+-------------+-------------------------------+-----------------------+---------+------+------+--------------------------------------------------------------+
2 rows in set (0.00 sec)
И это объяснение из подзапроса критериев hibernate:
+----+--------------------+-------+-----------------+---------------------------------------+---------+---------+------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+-----------------+---------------------------------------+---------+---------+------+-------+-------------+
| 1 | PRIMARY | this_ | ALL | ei3 | NULL | NULL | NULL | 49434 | Using where |
| 2 | DEPENDENT SUBQUERY | this_ | unique_subquery | PRIMARY,eijoin2,ei_client,team2,team1 | PRIMARY | 4 | func | 1 | Using where |
+----+--------------------+-------+-----------------+---------------------------------------+---------+---------+------+-------+-------------+
2 rows in set (0.00 sec)