web-dev-qa-db-fra.com

SQL Performance UNION vs OR

Je viens de lire une partie d'un article d'optimisation et segfaulted sur la déclaration suivante:

Lorsque vous utilisez SQL, remplacez les instructions à l'aide de OR par un UNION:

select username from users where company = ‘bbc’ or company = ‘iTV’;

à:

select username from users where company = ‘bbc’ union
select username from users where company = ‘iTV’;

À partir d'un EXPLAIN rapide:

Utilisation de OR:

enter image description here

Utilisation de UNION:

enter image description here

Cela ne signifie-t-il pas que UNION fait en doubler le travail?

Bien que j'apprécie que UNION puisse être plus performant pour certains SGBDR et certains schémas de table, ce n'est pas catégoriquement vrai comme le suggère l'auteur.

Question

Ai-je tort?

56
Jason McCreary

Soit l'article que vous lisez utilise un mauvais exemple, soit vous avez mal interprété leur propos.

select username from users where company = 'bbc' or company = 'iTV';

Cela équivaut à:

select username from users where company IN ('bbc', 'iTV');

MySQL peut très bien utiliser un index sur company pour cette requête. Il n'est pas nécessaire de faire UNION.

Le cas le plus délicat est celui où vous avez une condition OR qui implique deux colonnes différentes .

select username from users where company = 'bbc' or city = 'London';

Supposons qu'il existe un index sur company et un index séparé sur city. Étant donné que MySQL utilise généralement un seul index par table dans une requête donnée, quel index doit-il utiliser? S'il utilise l'index sur company, il devra quand même faire un scan de table pour trouver les lignes où city est Londres. S'il utilise l'index sur city, il devra faire un scan de table pour les lignes où company est bbc.

La solution UNION est pour ce type de cas.

select username from users where company = 'bbc' 
union
select username from users where city = 'London';

Désormais, chaque sous-requête peut utiliser l'index pour sa recherche, et les résultats de la sous-requête sont combinés par le UNION.


Un utilisateur anonyme a proposé une modification de ma réponse ci-dessus, mais un modérateur a rejeté la modification. Cela aurait dû être un commentaire, pas une modification. La revendication de la modification proposée était que UNION devait trier l'ensemble de résultats pour éliminer les lignes en double. Cela rend la requête plus lente et l'optimisation d'index est donc un lavage.

Ma réponse est que les index aident à réduire le jeu de résultats à un petit nombre de lignes avant que l'UNION ne se produise. UNION élimine en fait les doublons, mais pour ce faire, il suffit de trier le petit ensemble de résultats. Il peut y avoir des cas où les clauses WHERE correspondent à une partie importante de la table, et le tri pendant UNION est aussi coûteux que le simple fait de parcourir la table. Mais il est plus courant que l'ensemble de résultats soit réduit par les recherches indexées, donc le tri est beaucoup moins coûteux que l'analyse de table.

La différence dépend des données du tableau et des termes recherchés. La seule façon de déterminer la meilleure solution pour une requête donnée est d'essayer les deux méthodes dans le profileur de requêtes MySQL et de comparer leurs performances.

86
Bill Karwin

Ce ne sont pas la même requête.

Je n'ai pas beaucoup d'expérience avec MySQL, donc je ne suis pas sûr de ce que l'optimiseur de requête fait ou ne fait pas, mais voici mes réflexions sur mon expérience générale (principalement le serveur sql ms).

En règle générale, l'analyseur de requêtes peut prendre les deux requêtes ci-dessus et en faire exactement le même plan (si elles étaient les mêmes), donc cela n'aurait pas d'importance. Je soupçonne qu'il n'y a pas de différence de performances entre ces requêtes (qui sont équivalentes)

select distinct username from users where company = ‘bbc’ or company = ‘iTV’;

et

select username from users where company = ‘bbc’ 
union
select username from users where company = ‘iTV’;

Maintenant, la question est, y aurait-il une différence entre les requêtes suivantes, dont je ne sais pas réellement, mais je soupçonne que l'optimiseur la rendrait plus semblable à la première requête

select username from users where company = ‘bbc’ or company = ‘iTV’;

et

select username from users where company = ‘bbc’ 
union all
select username from users where company = ‘iTV’;
5
Darren Kopp

Cela dépend de ce que l'optimiseur finit par faire en fonction de la taille des données, des index, de la version du logiciel, etc.

Je suppose que l'utilisation de OR donnerait à l'optimiseur une meilleure chance de trouver des gains d'efficacité, car tout est dans une seule instruction logique.

De plus, UNION a des frais généraux, car il crée une réinitialisation set (pas de doublons). Chaque instruction dans l'UNION devrait s'exécuter assez rapidement si company est indexé ... je ne suis pas sûr qu'elle fasse vraiment double le travail.

Conclusion

À moins que vous n'ayez vraiment besoin de brûler chaque vitesse de votre requête, il est probablement préférable de choisir le formulaire qui communique le mieux votre intention ... le bloc opératoire

Mise à jour

Je voulais aussi mentionner IN. Je crois que la requête suivante donnera de meilleures performances que la OR (c'est aussi la forme que je préfère):

select username from users where company in ('bbc', 'iTV');

2
David J