2009-11-05 4 views
4

J'ai une requête assez complexe dans MySQL qui ralentit considérablement quand l'une des jointures est faite en utilisant un OR. Comment puis-je accélérer cela? la jointure correspondante est la suivante:Optimisation pour un OU dans une jointure dans MySQL

LEFT OUTER JOIN publications p ON p.id = virtual_performances.publication_id 
           OR p.shoot_id = shoots.id 

La suppression de l'une ou l'autre condition dans le bloc OR réduit le temps de requête de 1,5 s à 0,1 s. Il y a déjà des index sur toutes les colonnes pertinentes auxquelles je peux penser. Des idées? Les colonnes utilisées ont toutes des index. En utilisant EXPLAIN, j'ai découvert qu'une fois que l'OR arrive en jeu, MySQL finit par ne plus utiliser aucun des index. Y a-t-il un type particulier d'index que je peux faire qu'il utilisera?

Répondre

7

Ceci est une difficulté courante avec MySQL. L'utilisation de OR empêche l'optimiseur car il ne sait pas comment utiliser un index pour trouver une ligne où l'une ou l'autre de ces conditions est vraie. Je vais essayer d'expliquer: Supposons que je vous demande de rechercher un annuaire téléphonique et de trouver chaque personne dont le nom de famille est «Thomas» OU dont le prénom est «Thomas». Même si le répertoire téléphonique est essentiellement un index, vous n'en bénéficiez pas - vous devez effectuer une recherche page par page car il n'est pas trié par prénom. Gardez à l'esprit que dans MySQL, n'importe quelle instance d'une table dans une requête donnée peut utiliser un seul index, même si vous avez défini plusieurs index dans cette table. Une requête différente sur la même table peut utiliser un autre index si l'optimiseur explique que cela est plus utile.

Un peuple technique ont utilisé pour aider dans des situations comme votre est de faire un UNION de deux requêtes plus simples que chaque font usage d'indices distincts:

SELECT ... 
FROM virtual_performances v 
JOIN shoots s ON (...) 
LEFT OUTER JOIN publications p ON (p.id = v.publication_id) 
UNION ALL 
SELECT ... 
FROM virtual_performances v 
JOIN shoots s ON (...) 
LEFT OUTER JOIN publications p ON p.shoot_id = s.id; 
+0

Est-ce vrai aussi pour PostgreSQL? S'il vous plaît voir ma question ici si vous êtes familier: http://stackoverflow.com/questions/1677538/advanced-indexing-involving-or-ed-conditions-pgsql –

+0

J'ai posté une réponse à votre autre question. –

4

Effectuez deux jointures sur la même table (en ajoutant des alias pour les séparer) pour les deux conditions, et voyez si cela est plus rapide.

select ..., coalesce(p1.field, p2.field) as field 
from ... 
left join publications p1 on p1.id = virtual_performances.publication_id 
left join publications p2 on p2.shoot_id = shoots.id 
0

Vous pouvez également essayer quelque chose comme ceci sur la taille:

SELECT * FROM tablename WHERE id IN (SELECT p.id FROM tablename LEFT OUTER JOIN publications p ON p.id IN virtual_performances.publication_id) OR p.id IN (SELECT p.id FROM tablename LEFT OUTER JOIN publications p ON p.shoot_id = shoots.id);

Il est un peu messier, et ne sera pas plus rapide dans tous les cas, mais MySQL est bon à la sélection des ensembles de données droites, donc vous répéter est pas si mal.