2010-10-11 16 views
3

Je suis tombé sur une requête qui prend "trop ​​long". La requête a plus de 50 jointures entre 10 tables ou plus. Pour donner un bref aperçu du modèle de base de données, les tables jointes sont des tables qui stockent des données pour un type de données particulier (ex: date_fields, integer_fields, text_fields, etc.) et chacune a une colonne pour la valeur, un identifiant "datafield", et un identifiant de ticket. La requête est construite par programme sur la base d'une table d'association entre un "ticket" et ses "champs de données".Optimiser une requête qui utilise plusieurs jointures à gauche sur les mêmes tables

La jointure des déclarations ressemblent à ce qui suit:

...FROM tickets t 
LEFT JOIN ticket_text_fields t001 ON(t.id=t001.ticket_id AND t001.textfield_id=7) 
... 
LEFT JOIN ticket_date_fields t056 ON(t.id=t056.ticket_id AND t056.datafield_id=434) 

Lorsque vous utilisez expliquer sur la requête indique ce qui suit:

1 SIMPLE t  ref idx_dataset_id     idx_dataset_id 5 const 2871 Using where; Using temporary; Using filesort 
1 SIMPLE t001 ref idx_ticket_id,idx_datafield_id idx_ticket_id 5 t.id 5 
... 
1 SIMPLE t056 ref idx_ticket_id,idx_datafield_id idx_ticket_id 5 t.id 8 

Quelle direction puis-je prendre pour régler cette question? Tous les index semblent être en place. Peut-être que le numéro de ligne de la table t (tickets) (2871) devrait être réduit. Combien de jointures gauche est trop? Les tables de datafield doivent-elles être jointes une seule fois, puis interrogées chacune pour les données requises?

Répondre

7

Vous utilisez une variante du terrible antipattern appelé Entity-Attribute-Value. Vous stockez des attributs sur des lignes séparées, donc si vous voulez reconstruire quelque chose qui ressemble à une rangée de données conventionnelle, vous devez faire une jointure par attribut.

Il n'est pas surprenant que cela crée une requête avec 50 jointures. C'est beaucoup trop pour que la plupart des bases de données fonctionnent efficacement (vous n'avez pas identifié la base de données que vous utilisez). Finalement, vous aurez besoin de quelques attributs supplémentaires et vous pourriez dépasser certaines limites architecturales de la base de données sur le nombre de jointures qu'il peut faire.

La solution est: ne pas reconstruire la ligne dans SQL.

À la place, interrogez les attributs en tant que lignes multiples, au lieu d'essayer de les combiner sur une seule ligne.

SELECT ... FROM tickets t 
INNER JOIN ticket_text_fields f ON t.id=f.ticket_id 
WHERE f.textfield_id IN (7, 8, 9, ...) 
UNION ALL 
SELECT ... FROM tickets t 
INNER JOIN ticket_date_fields d ON t.id=d.ticket_id 
WHERE d.datafield_id IN (434, 435, 436, ...) 

Ensuite, vous devez écrire une fonction dans votre application en boucle sur l'ensemble de lignes résultant, et de recueillir les attributs un par un dans un objet dans l'espace d'application, alors vous pouvez l'utiliser comme si elle est une seule entité .

+1

Bonne réponse Bill! Je ne pouvais pas penser à une bonne solution sans changer le schéma, alors j'ai continué à recharger cette question pour voir ce que les autres diraient. J'aime ta solution. –

+0

D'accord. Merci pour l'info et la solution! –

0

pour la requête plus claire je voudrais utiliser quelque chose comme ceci:

SELECT ... FROM tickets as t 
JOIN ticket_text_fields as txt ON t.id = txt.ticket_id 
JOIN ticket_date_fields as dt ON t.id = dt.ticket_id 
WHERE txt.textfield_id IN (...) 
AND dt.datefield_id IN (...) 

Joins serait probablement gauche, mais cela dépend de la structure de vos données.
Il n'y a pas d'union dans la requête et il n'y a que deux jointures