2012-01-09 4 views
0

Je rencontre un problème avec une requête complexe avec plusieurs jointures. Lors de l'exécution EXPLIQUER:Jointure extrêmement lente avec le tampon de jointure

Recherche

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

Je reçois ceci:

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" 

Le modèle de données est comme ceci:

compte < 1- > Lieu < 1-> Événement 0 Le ticket UserDetail a un compte, un lieu, un événement ou une liste d'invités en tant que parent.

Et ce que j'essaye de faire avec cette requête est d'obtenir tout le UserDetail qui a un compte spécifique/lieu/événement/guestlist en tant que parent, OU qui a une guestlist en tant que parent qui a un ticket dont le champ reference_user est défini sur un utilisateur spécifique.

critères Hibernate

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(); 
} 

D'après ce que je peux voir la dernière ligne est le problème, mais comment puis-je résoudre ce problème? Cette requête est générée automatiquement par hibernate, donc je ne suis pas sûr de combien je peux le modifier.

+0

avez-vous des indices sur la table user_detail? –

+0

Oui, toutes les colonnes utilisées sont indexées ou étrangères –

+0

Pourquoi diable utilisez-vous des jointures croisées pour cela? – HLGEM

Répondre

1

Votre surutilisation de jointures cartésiennes à jointure croisée n'a pas de sens ... Qu'est-ce que vous cherchez réellement? Puisque vos clauses "OR" sont toutes basées sur cette valeur de 10, mais en faisant une jointure implicite à la table des tickets par l'identifiant guest_list - et enfin en exigeant la t.guest_list = 10?

Étant donné que toutes vos jointures internes regardent AUSSI la table de détail de l'utilisateur d'origine ayant la même valeur que le résultat de la jointure. Votre kicker est que la finale « et » cherche spécifiquement guest_list = 10. Je commence immédiatement avec comme base et OU les autres ... Je pourrais considérer les points suivants:

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) 

Vous faites une référence à un "Reference_User = 10", mais quel est ce contexte ... est-ce que comme un détail de l'utilisateur a un invité? et cet invité peut-il être associé au même détail d'événement/site/compte utilisateur?

En fournissant un certain échantillon des détails et des précisions sur ce que vous espérez obtenir vous obtiendrez beaucoup plus loin ...

+0

DRapp, s'il vous plaît vérifier à nouveau ma question - J'ai ajouté plus d'informations. La requête elle-même est générée par Hibernate donc ce n'était pas un choix personnel. L'ordre des critères est-il important? –

+0

@PiotrBlasiak, désolé retard ... Sans voir les données réelles, et un meilleur contexte pour les relations, je ne pense pas que je peux vous offrir beaucoup plus. – DRapp