Очень медленное соединение с буфером соединения - PullRequest
0 голосов
/ 09 января 2012

У меня проблема со сложным запросом с несколькими объединениями. При запуске EXPLAIN:

Запрос

explain

select ud.id from user_detail ud
cross join ticket t
cross join guest_list gl
cross join event e
cross join venue v

where t.guest_list = gl.id and gl.event = e.id and e.venue = v.id
and (ud.account = 10 or ud.venue = 10 or ud.event = 10 or ud.guest_list = 10 or t.reference_user = 10 and (ud.guest_list=t.guest_list or ud.event = gl.event or ud.venue = e.venue or ud.account = v.account) and (t.guest_list = 10))

Я получаю это:

id, select_type, table, type, rows, extra
1, SIMPLE, v, index, 2, "Using index"
1, SIMPLE, e, ref, 2, "Using where; using index"
1, SIMPLE, gl, ref, 1, "Using where; using index"
1, SIMPLE, t, ref, 418, "Using where"
1, SIMPLE, ud, ALL, 44028, "Using where; Using join buffer"

Модель данных выглядит следующим образом:

Аккаунт <1- <em>> Место проведения <1- </em>> Событие <1- <em>> Гостевой список <1- </em>> Билет UserDetail имеет учетную запись, место проведения, событие или список гостей в качестве родителя.

И то, что я пытаюсь сделать с этим запросом, - это получить весь UserDetail, у которого в качестве родителя есть одна из учетных записей / мест / событий / гостей, ИЛИ с гостевым списком в качестве родителя, у которого есть билет, который имеет поле reference_user, установленное для конкретного пользователя.

Критерии гибернации

public List<UserDetail> listUserDetails(final Collection<UserDetailNode> anyOfNodes, final User orTicketReferenceUser, final Collection<GuestList> andAnyOfGuestlistsForTicketReferenceUser, final Collection<User> anyOfUsers, final Date fromLastModificationDate, final Date toLastModificationDate, final Boolean deletedNodes, final Boolean deletedUsers, final Boolean deletedUserDetails) {

    final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    final CriteriaQuery<UserDetail> cq = cb.createQuery(UserDetail.class);
    final Root<UserDetail> userDetail = cq.from(UserDetail.class);
    Predicate criteria = cb.conjunction();

    if (anyOfNodes != null || orTicketReferenceUser != null) {

        Predicate subCriteria = cb.disjunction();

        if (anyOfNodes != null) {

            Predicate anyOfNodesCriteria = cb.disjunction();

            Collection<Account> anyOfAccounts = null;
            Collection<Venue> anyOfVenues = null;
            Collection<Event> anyOfEvents = null;
            Collection<GuestList> anyOfGuestLists = null;

            final Set<UserDetailNode> anyOfNodesWithParents = new HashSet<UserDetailNode>();
            for (UserDetailNode node : anyOfNodes) {

                while (node != null) {

                    anyOfNodesWithParents.add(node);
                    node = node.getParentNode();
                }
            }

            for (final UserDetailNode node : anyOfNodesWithParents) {

                if (node instanceof Account) {

                    if (anyOfAccounts == null) anyOfAccounts = new ArrayList<Account>();
                    anyOfAccounts.add((Account)node);
                }
                else if (node instanceof Venue) {

                    if (anyOfVenues == null) anyOfVenues = new ArrayList<Venue>();
                    anyOfVenues.add((Venue)node);
                }
                else if (node instanceof Event) {

                    if (anyOfEvents == null) anyOfEvents = new ArrayList<Event>();
                    anyOfEvents.add((Event)node);
                }
                else if (node instanceof GuestList) {

                    if (anyOfGuestLists == null) anyOfGuestLists = new ArrayList<GuestList>();
                    anyOfGuestLists.add((GuestList)node);
                }
            }

            if (anyOfAccounts != null) anyOfNodesCriteria = cb.or(anyOfNodesCriteria, cb.or(userDetail.get("account").in(anyOfAccounts)));
            if (anyOfVenues != null) anyOfNodesCriteria = cb.or(anyOfNodesCriteria, cb.or(userDetail.get("venue").in(anyOfVenues)));
            if (anyOfEvents != null) anyOfNodesCriteria = cb.or(anyOfNodesCriteria, cb.or(userDetail.get("event").in(anyOfEvents)));
            if (anyOfGuestLists != null) anyOfNodesCriteria = cb.or(anyOfNodesCriteria, cb.or(userDetail.get("guestList").in(anyOfGuestLists)));

            subCriteria = cb.or(subCriteria, anyOfNodesCriteria);
        }

        if (orTicketReferenceUser != null && (andAnyOfGuestlistsForTicketReferenceUser == null || !andAnyOfGuestlistsForTicketReferenceUser.isEmpty())) {

            final Root<Ticket> ticket = cq.from(Ticket.class);
            Predicate ticketCriteria = cb.equal(ticket.get("referenceUser"), orTicketReferenceUser);
            ticketCriteria = cb.and(ticketCriteria, cb.or(cb.equal(userDetail.get("guestList"), ticket.get("guestList")), cb.equal(userDetail.get("event"), ticket.get("guestList").get("event")), cb.equal(userDetail.get("venue"), ticket.get("guestList").get("event").get("venue")), cb.equal(userDetail.get("account"), ticket.get("guestList").get("event").get("venue").get("account"))));

            if (andAnyOfGuestlistsForTicketReferenceUser != null) ticketCriteria = cb.and(ticketCriteria, ticket.get("guestList").in(andAnyOfGuestlistsForTicketReferenceUser));

            subCriteria = cb.or(subCriteria, ticketCriteria);
        }

        criteria = cb.and(criteria, subCriteria);
    }

    if (anyOfUsers != null) {

        if (anyOfUsers.isEmpty()) return new ArrayList<UserDetail>();
        criteria = cb.and(criteria, userDetail.get("user").in(anyOfUsers));
    }

    if (fromLastModificationDate != null) criteria = cb.and(criteria, cb.greaterThanOrEqualTo(userDetail.<Date>get("lastModificationDate"), fromLastModificationDate));
    if (toLastModificationDate != null) criteria = cb.and(criteria, cb.lessThanOrEqualTo(userDetail.<Date>get("lastModificationDate"), toLastModificationDate));

    cq.select(userDetail).distinct(true).where(criteria);

    return entityManager.createQuery(cq).getResultList();
}

Из того, что я вижу, последний ряд - проблема, но как я могу это исправить? Этот запрос автоматически генерируется hibernate, поэтому я не уверен, насколько сильно я могу его изменить.

1 Ответ

1 голос
/ 09 января 2012

Ваше чрезмерное использование перекрестных декартовых объединений не имеет смысла ... Что именно вы на самом деле ищете. Поскольку все ваши предложения «ИЛИ» основаны на этом значении 10, но затем выполняются неявные соединения с таблицей заявок по идентификатору guest_list - и, наконец, ТРЕБУЕТСЯ t.guest_list = 10?

Поскольку все ваши внутренние объединения ТАКЖЕ смотрят на исходную таблицу сведений о пользователе, имеющую то же значение, что и результат объединения. Ваш кикер в том, что ФИНАЛЬНОЕ «И» специально ищет guest_list = 10. Я бы сразу начал с этого как основы и ИЛИ других ... Я мог бы рассмотреть следующее:

select STRAIGHT_JOIN
      ud.id 
   from 
      ticket t
         JOIN user_detail ud
            ON t.guest_list = ud.guest_list
   where 
          t.guest_list = 10
      AND (   ud.account = 10 
           or ud.venue = 10 
           or ud.event = 10 )

Вы делаете ссылку на "Reference_User = 10", но что это за контекст ... это как будто одна деталь пользователя имеет гостя? и этот гость может быть связан с тем же событием / местом проведения / учетной записью пользователя?

Предоставив образец деталей и уточнив, что вы надеетесь получить, вы значительно продвинетесь вперед ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...