web-dev-qa-db-fra.com

Joindre vs sous-requête

Je suis un utilisateur MySQL de la vieille école et j'ai toujours préféré JOIN à la sous-requête. Mais de nos jours, tout le monde utilise la sous-requête et je la déteste; Je ne sais pas pourquoi. 

Il me manque les connaissances théoriques pour juger par moi-même s'il y a une différence. Une sous-requête vaut-elle aussi une JOIN et n’y at-il donc rien à craindre?

696

Extrait du manuel MySQL ( 13.2.10.11 Réécriture des sous-requêtes en jointures ):

Une jointure gauche [OUTER] JOIN peut être plus rapide qu'une sous-requête équivalente car le serveur pourrait mieux l'optimiser, ce qui n'est pas spécifique au serveur MySQL.

Donc, les sous-requêtes peuvent être plus lentes que LEFT [OUTER] JOINS, mais à mon avis, leur force est d'une lisibilité légèrement supérieure.

128
simhumileco

Les sous-requêtes sont le moyen logiquement correct de résoudre les problèmes de la forme "Obtenir des faits de A, conditionnés par des faits de B". Dans de tels cas, il est plus logique de coller B dans une sous-requête que de faire une jointure. C'est également plus sûr, sur le plan pratique, car il n'est pas nécessaire d'être prudent pour obtenir des doublons de A en raison de plusieurs matchs contre B.

En pratique, cependant, la réponse est généralement liée à la performance. Certains optimiseurs sucent des citrons lorsqu'on leur donne une jointure par rapport à une sous-requête, tandis que d'autres sucent des citrons de l'autre, et ceci est spécifique à l'optimiseur, à la version du SGBD et à la requête.

Historiquement, les jointures explicites sont généralement gagnantes, d’où la sagesse bien établie selon laquelle les jointures sont meilleures, mais les optimiseurs s’améliorent constamment. Je préfère donc écrire des requêtes de manière cohérente et logique, puis les restructurer si les contraintes de performances le justifient.

740
Marcelo Cantos

Dans la plupart des cas, les JOINs sont plus rapides que les sous-requêtes et il est très rare qu'une sous-requête soit plus rapide.

Dans JOINs, le SGBDR peut créer un plan d'exécution mieux adapté à votre requête et prévoir les données à charger pour le traitement et gagner du temps, contrairement à la sous-requête dans laquelle toutes les requêtes sont exécutées et toutes les données chargées du traitement .

La bonne chose dans les sous-requêtes est qu’elles sont plus lisibles que JOINs: c’est pourquoi la plupart des nouveaux utilisateurs de SQL les préfèrent; c'est le moyen facile; mais quand il s'agit de performance, JOINS est meilleur dans la plupart des cas, même s’ils ne sont pas difficiles à lire aussi.

338
Kronass

Utilisez EXPLAIN pour voir comment votre base de données exécute la requête sur vos données. Il y a un énorme "ça dépend" dans cette réponse ...

PostgreSQL peut réécrire une sous-requête sur une jointure ou une jointure sur une sous-requête quand il pense que l'une est plus rapide que l'autre. Tout dépend des données, des index, de la corrélation, de la quantité de données, de la requête, etc.

119
Frank Heikens

Tout d'abord, pour comparer les deux premiers, vous devez distinguer les requêtes avec des sous-requêtes en: 

  1. une classe de sous-requêtes qui ont toujours une requête équivalente correspondante écrite avec des jointures 
  2. une classe de sous-requêtes qui ne peuvent pas être réécrites à l'aide de jointures

Pour la première classe des requêtes, un bon SGBDR considère les jointures et les sous-requêtes comme équivalentes et produit les mêmes plans de requête.

De nos jours, même MySQL le fait.

Pourtant, parfois, ce n'est pas le cas, mais cela ne signifie pas que les jointures seront toujours gagnantes. J'avais des cas d'utilisation de sous-requêtes dans les performances améliorées de MySQL. (Par exemple, si quelque chose empêche le planificateur mysql d'estimer correctement le coût et si le planificateur ne voit pas les variantes de jointure et de sous-requête comme identiques, les sous-requêtes peuvent surperformer les jointures en forçant un certain chemin). 

La conclusion est que vous devez tester vos requêtes pour les variantes de jointure et de sous-requête si vous voulez savoir laquelle fonctionnera le mieux.

Pour la deuxième classe la comparaison n'a aucun sens, car ces requêtes ne peuvent pas être réécrites à l'aide de jointures. Dans ce cas, les sous-requêtes constituent un moyen naturel d'exécuter les tâches requises et vous ne devez pas les discriminer.

40
Unreason

En 2010, j'aurais rejoint l'auteur de cette question et aurais fortement voté pour JOIN. Mais avec beaucoup plus d'expérience (en particulier dans MySQL), je peux affirmer: Oui, les sous-requêtes peuvent être meilleures. J'ai lu plusieurs réponses ici. Certains ont déclaré que les sous-requêtes sont plus rapides, mais il manquait une bonne explication. J'espère que je pourrai fournir à cette réponse (très) tardive:

Tout d’abord, laissez-moi dire le plus important: Il existe différentes formes de sous-requêtes

Et la deuxième déclaration importante: La taille compte

Si vous utilisez des sous-requêtes, vous devez savoir comment le serveur de base de données exécute la sous-requête. Surtout si la sous-requête est évaluée une fois ou toutes les lignes!!. De l'autre côté, un serveur de base de données moderne est capable d'optimiser beaucoup. Dans certains cas, une sous-requête permet d'optimiser une requête, mais une version plus récente du serveur de base de données peut rendre l'optimisation obsolète.

Sous-requêtes dans les champs sélectionnés

SELECT moo, (SELECT roger FROM wilco WHERE moo = me) AS bar FROM foo

Sachez qu'une sous-requête est exécutée pour chaque ligne résultante de foo. Évitez cela si possible, cela pourrait ralentir considérablement votre requête sur des jeux de données volumineux. Toutefois, si la sous-requête ne contient aucune référence à foo, elle peut être optimisée par le serveur de base de données en tant que contenu statique et ne peut être évaluée qu'une seule fois.

Sous-requêtes dans l'instruction Where

SELECT moo FROM foo WHERE bar = (SELECT roger FROM wilco WHERE moo = me)

Si vous avez de la chance, la base de données l’optimise en interne dans une variable JOIN. Sinon, votre requête deviendra très très lente sur des jeux de données volumineux, car elle exécutera la sous-requête pour chaque ligne dans foo, et pas seulement les résultats comme dans le type-type.

Sous-requêtes dans l'instruction Join

SELECT moo, bar 
  FROM foo 
    LEFT JOIN (
      SELECT MIN(bar), me FROM wilco GROUP BY me
    ) ON moo = me

C'est intéressant. Nous combinons JOIN avec une sous-requête. Et nous obtenons ici la force réelle des sous-requêtes. Imaginez un ensemble de données avec des millions de lignes dans wilco mais seulement quelques me distincts. Au lieu de rejoindre une table immense, nous avons maintenant une table temporaire plus petite. Cela peut entraîner des requêtes beaucoup plus rapides, en fonction de la taille de la base de données. Vous pouvez avoir le même effet avec CREATE TEMPORARY TABLE ... et INSERT INTO ... SELECT ..., qui pourraient fournir une meilleure lisibilité pour les requêtes très complexes (mais peuvent verrouiller les jeux de données dans un niveau d'isolation en lecture répétable).

Sous-requêtes imbriquées

SELECT moo, bar
  FROM (
    SELECT moo, CONCAT(roger, wilco) AS bar
      FROM foo
      GROUP BY moo
      HAVING bar LIKE 'SpaceQ%'
  ) AS temp_foo
  GROUP BY bar
  ORDER BY bar

Vous pouvez imbriquer des sous-requêtes dans plusieurs niveaux. Cela peut aider sur d’énormes jeux de données, si vous devez regrouper ou trier les résultats. En règle générale, le serveur de base de données crée une table temporaire à cet effet, mais vous n'avez parfois pas besoin de trier l'ensemble de la table, mais uniquement le jeu de résultats. Cela pourrait fournir de bien meilleures performances en fonction de la taille de la table.

Conclusion

Les sous-requêtes ne remplacent pas une JOIN et vous ne devriez pas les utiliser comme ceci (bien que possible). À mon humble avis, l’utilisation correcte d’une sous-requête est l’utilisation en remplacement rapide de CREATE TEMPORARY TABLE .... Une bonne sous-requête réduit un ensemble de données d'une manière que vous ne pouvez pas accomplir dans une instruction ON d'une JOIN. Si une sous-requête comporte l'un des mots clés GROUP BY ou DISTINCT et qu'elle ne se trouve de préférence pas dans les champs de sélection ou dans l'instruction where, elle peut améliorer considérablement les performances.

26
Trendfischer

La documentation MSDN pour SQL Server indique

De nombreuses instructions Transact-SQL comprenant des sous-requêtes peuvent également être formulées en jointures. D'autres questions ne peuvent être posées qu'avec des sous-requêtes. Dans Transact-SQL, il n'y a généralement pas de différence de performances entre une instruction qui inclut une sous-requête et une version sémantiquement équivalente qui n'en contient pas. Cependant, dans certains cas où l'existence doit être vérifiée, une jointure offre de meilleures performances. Sinon, la requête imbriquée doit être traitée pour chaque résultat de la requête externe afin de garantir l'élimination des doublons. Dans de tels cas, une approche de jointure donnerait de meilleurs résultats.

donc si vous avez besoin de quelque chose comme 

select * from t1 where exists select * from t2 where t2.parent=t1.id

essayez plutôt d'utiliser join. Dans d'autres cas, cela ne fait aucune différence.

Je dis: La création de functions pour les sous-requêtes élimine le problème de fouillis et vous permet d'implémenter une logique supplémentaire pour les sous-requêtes. Je recommande donc de créer des fonctions pour les sous-requêtes chaque fois que cela est possible. 

L'encombrement dans le code est un gros problème et l'industrie s'efforce de l'éviter depuis des décennies.

22
Uğur Gümüşhan

Je pense que ce qui a été sous-souligné dans les réponses citées est la question des doublons et des résultats problématiques pouvant découler de cas (d'utilisation) spécifiques.

(bien que Marcelo Cantos le mentionne)

Je citerai l'exemple tiré des cours Lagunita de Stanford sur SQL.

Table d'étudiant

+------+--------+------+--------+
| sID  | sName  | GPA  | sizeHS |
+------+--------+------+--------+
|  123 | Amy    |  3.9 |   1000 |
|  234 | Bob    |  3.6 |   1500 |
|  345 | Craig  |  3.5 |    500 |
|  456 | Doris  |  3.9 |   1000 |
|  567 | Edward |  2.9 |   2000 |
|  678 | Fay    |  3.8 |    200 |
|  789 | Gary   |  3.4 |    800 |
|  987 | Helen  |  3.7 |    800 |
|  876 | Irene  |  3.9 |    400 |
|  765 | Jay    |  2.9 |   1500 |
|  654 | Amy    |  3.9 |   1000 |
|  543 | Craig  |  3.4 |   2000 |
+------+--------+------+--------+

Appliquer la table

(applications faites à des universités et des majors spécifiques)

+------+----------+----------------+----------+
| sID  | cName    | major          | decision |
+------+----------+----------------+----------+
|  123 | Stanford | CS             | Y        |
|  123 | Stanford | EE             | N        |
|  123 | Berkeley | CS             | Y        |
|  123 | Cornell  | EE             | Y        |
|  234 | Berkeley | biology        | N        |
|  345 | MIT      | bioengineering | Y        |
|  345 | Cornell  | bioengineering | N        |
|  345 | Cornell  | CS             | Y        |
|  345 | Cornell  | EE             | N        |
|  678 | Stanford | history        | Y        |
|  987 | Stanford | CS             | Y        |
|  987 | Berkeley | CS             | Y        |
|  876 | Stanford | CS             | N        |
|  876 | MIT      | biology        | Y        |
|  876 | MIT      | marine biology | N        |
|  765 | Stanford | history        | Y        |
|  765 | Cornell  | history        | N        |
|  765 | Cornell  | psychology     | Y        |
|  543 | MIT      | CS             | N        |
+------+----------+----------------+----------+

Essayons de trouver les scores GPA pour les étudiants qui ont appliqué à CS major (quelle que soit l'université)

Utilisation d'une sous-requête:

select GPA from Student where sID in (select sID from Apply where major = 'CS');

+------+
| GPA  |
+------+
|  3.9 |
|  3.5 |
|  3.7 |
|  3.9 |
|  3.4 |
+------+

La valeur moyenne pour cet ensemble de résultats est:

select avg(GPA) from Student where sID in (select sID from Apply where major = 'CS');

+--------------------+
| avg(GPA)           |
+--------------------+
| 3.6800000000000006 |
+--------------------+

Utilisation d'une jointure:

select GPA from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';

+------+
| GPA  |
+------+
|  3.9 |
|  3.9 |
|  3.5 |
|  3.7 |
|  3.7 |
|  3.9 |
|  3.4 |
+------+

valeur moyenne pour cet ensemble de résultats:

select avg(GPA) from Student, Apply where Student.sID = Apply.sID and Apply.major = 'CS';

+-------------------+
| avg(GPA)          |
+-------------------+
| 3.714285714285714 |
+-------------------+

Il est évident que la deuxième tentative produit des résultats trompeurs dans notre cas d'utilisation, car elle compte les doublons pour le calcul de la valeur moyenne ..__ Il est également évident que l'utilisation de distinct avec l'instruction basée sur la jointure sera non élimine le problème, étant donné qu’il conservera par erreur une occurrence sur trois du score 3.9. Le cas correct est de prendre en compte TWO (2) occurrences du score 3.9, étant donné que nous avons réellement TWO (2) étudiants avec ce score conformes à nos critères de recherche.

Il semble que, dans certains cas, une sous-requête constitue la solution la plus sûre, en dehors de tout problème de performances.

20
pkaramol

Exécutez-vous sur une très grande base de données à partir d'un ancien CMS Mambo:

SELECT id, alias
FROM
  mos_categories
WHERE
  id IN (
    SELECT
      DISTINCT catid
    FROM mos_content
  );

0 secondes

SELECT
  DISTINCT mos_content.catid,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;

~ 3 secondes

Un EXPLAIN montre qu’ils examinent exactement le même nombre de lignes, mais l’une prend 3 secondes et l’autre est quasi instantanée. Morale de l'histoire? Si la performance est importante (quand n'est-ce pas?), Essayez-la de plusieurs façons et voyez laquelle est la plus rapide.

Et...

SELECT
  DISTINCT mos_categories.id,
  mos_categories.alias
FROM
  mos_content, mos_categories
WHERE
  mos_content.catid = mos_categories.id;

0 secondes

Encore une fois, mêmes résultats, même nombre de lignes examinées. Je pense que DISTINCT mos_content.catid prend beaucoup plus de temps à comprendre que DISTINCT mos_categories.id.

15
Jason

Les sous-requêtes sont généralement utilisées pour renvoyer une seule ligne sous forme de valeur atomique, bien qu'elles puissent être utilisées pour comparer des valeurs à plusieurs lignes avec le mot clé IN. Ils sont autorisés à presque n'importe quel moment significatif d'une instruction SQL, y compris la liste de cibles, la clause WHERE, etc. Une simple sous-requête peut être utilisée comme condition de recherche. Par exemple, entre deux tables:

   SELECT title FROM books WHERE author_id = (SELECT id FROM authors WHERE last_name = 'Bar' AND first_name = 'Foo');

Notez que l'utilisation d'un opérateur de valeur normale sur les résultats d'une sous-requête nécessite qu'un seul champ soit renvoyé. Si vous souhaitez vérifier l'existence d'une valeur unique dans un ensemble d'autres valeurs, utilisez IN:

   SELECT title FROM books WHERE author_id IN (SELECT id FROM authors WHERE last_name ~ '^[A-E]');

Ceci est évidemment différent de dire un LEFT-JOIN où vous voulez juste joindre des trucs des tables A et B même si la condition de jointure ne trouve aucun enregistrement correspondant dans la table B, etc.

Si vous vous inquiétez simplement de la rapidité, vous devrez vérifier votre base de données et rédiger une bonne requête afin de voir s’il existe une différence significative en termes de performances.

12
rkulla

Selon mon observation, comme dans deux cas, si une table a moins de 100 000 enregistrements, la jointure fonctionnera rapidement.

Mais dans le cas où une table a plus de 100 000 tables, une sous-requête est le meilleur résultat. 

J'ai une table qui a 500 000 enregistrements sur que j'ai créé ci-dessous la requête et son temps de résultat est comme

SELECT * 
FROM crv.workorder_details wd 
inner join  crv.workorder wr on wr.workorder_id = wd.workorder_id;

Résultat: 13.3 secondes

select * 
from crv.workorder_details 
where workorder_id in (select workorder_id from crv.workorder)

Résultat: 1,65 secondes

12
Vijay Gajera

Les sous-requêtes ont la capacité de calculer des fonctions d'agrégation à la volée. Trouvez le prix minimal du livre et obtenez tous les livres vendus avec ce prix . 1) Utilisation des sous-requêtes:

SELECT titles, price
FROM Books, Orders
WHERE price = 
(SELECT MIN(price)
 FROM Orders) AND (Books.ID=Orders.ID);

2) en utilisant des JOIN

SELECT MIN(price)
     FROM Orders;
-----------------
2.99

SELECT titles, price
FROM Books b
INNER JOIN  Orders o
ON b.ID = o.ID
WHERE o.price = 2.99;
11
Vlad

Version de MySQL: 5.5.28-0ubuntu0.12.04.2-log

J'avais aussi l'impression que JOIN est toujours préférable à une sous-requête dans MySQL, mais EXPLAIN est un meilleur moyen de porter un jugement. Voici un exemple où les sous-requêtes fonctionnent mieux que les JOIN.

Voici ma requête avec 3 sous-requêtes:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=43) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=55) IS NULL 
 AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;

EXPLAIN montre:

+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
| id | select_type        | table    | type   | possible_keys                                       | key          | key_len | ref                                             | rows | Extra                    |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+
|  1 | PRIMARY            | vrl      | index  | PRIMARY                                             | moved_date   | 8       | NULL                                            |  200 | Using where              |
|  1 | PRIMARY            | l        | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  1 | PRIMARY            | vrlih    | eq_ref | PRIMARY                                             | PRIMARY      | 9       | ranker.vrl.list_id,ranker.vrl.ontology_id,const |    1 | Using where              |
|  1 | PRIMARY            | lbs      | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                              |    1 | Using where              |
|  4 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  3 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | list_tag | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.l.list_id,const                          |    1 | Using where; Using index |
+----+--------------------+----------+--------+-----------------------------------------------------+--------------+---------+-------------------------------------------------+------+--------------------------+

La même requête avec JOIN est:

EXPLAIN SELECT vrl.list_id,vrl.ontology_id,vrl.position,l.name AS list_name, vrlih.position AS previous_position, vrl.moved_date 
FROM `vote-ranked-listory` vrl 
INNER JOIN lists l ON l.list_id = vrl.list_id 
INNER JOIN `vote-ranked-list-item-history` vrlih ON vrl.list_id = vrlih.list_id AND vrl.ontology_id=vrlih.ontology_id AND vrlih.type='PREVIOUS_POSITION' 
INNER JOIN list_burial_state lbs ON lbs.list_id = vrl.list_id AND lbs.burial_score < 0.5 
LEFT JOIN list_tag lt1 ON lt1.list_id = vrl.list_id AND lt1.tag_id = 43 
LEFT JOIN list_tag lt2 ON lt2.list_id = vrl.list_id AND lt2.tag_id = 55 
INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403 
WHERE vrl.position <= 15 AND l.status='ACTIVE' AND l.is_public=1 AND vrl.ontology_id < 1000000000 
AND lt1.list_id IS NULL AND lt2.tag_id IS NULL 
ORDER BY vrl.moved_date DESC LIMIT 200;

et le résultat est:

+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                                       | key          | key_len | ref                                         | rows | Extra                                        |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | lt3   | ref    | list_tag_key,list_id,tag_id                         | tag_id       | 5       | const                                       | 2386 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | l     | eq_ref | PRIMARY,status,ispublic,idx_lookup,is_public_status | PRIMARY      | 4       | ranker.lt3.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | vrlih | ref    | PRIMARY                                             | PRIMARY      | 4       | ranker.lt3.list_id                          |  103 | Using where                                  |
|  1 | SIMPLE      | vrl   | ref    | PRIMARY                                             | PRIMARY      | 8       | ranker.lt3.list_id,ranker.vrlih.ontology_id |   65 | Using where                                  |
|  1 | SIMPLE      | lt1   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index; Not exists         |
|  1 | SIMPLE      | lbs   | eq_ref | PRIMARY,idx_list_burial_state,burial_score          | PRIMARY      | 4       | ranker.vrl.list_id                          |    1 | Using where                                  |
|  1 | SIMPLE      | lt2   | ref    | list_tag_key,list_id,tag_id                         | list_tag_key | 9       | ranker.lt3.list_id,const                    |    1 | Using where; Using index                     |
+----+-------------+-------+--------+-----------------------------------------------------+--------------+---------+---------------------------------------------+------+----------------------------------------------+

Une comparaison de la colonne rows indique la différence et la requête avec JOIN utilise Using temporary; Using filesort.

Bien sûr, lorsque je lance les deux requêtes, la première se fait en 0,02 seconde, la seconde ne se termine pas même après une minute, donc EXPLAIN a correctement expliqué ces requêtes.

Si je n'ai pas INNER JOIN sur la table list_tag c'est-à-dire si je supprime 

AND (SELECT list_id FROM list_tag WHERE list_id=l.list_id AND tag_id=246403) IS NOT NULL  

à partir de la première requête et en conséquence:

INNER JOIN list_tag lt3 ON lt3.list_id = vrl.list_id AND lt3.tag_id = 246403

à partir de la deuxième requête, puis EXPLAIN renvoie le même nombre de lignes pour les deux requêtes et ces deux requêtes s'exécutent à la même vitesse.

11
arun
  • Une règle générale est que jointures sont plus rapides dans la plupart des cas (99%).
  • Plus il y a de tables de données, les sous-requêtes sont plus lentes.
  • Moins il y a de tables de données, les sous-requêtes ont une vitesse équivalente à jointures.
  • Les sous-requêtes sont plus simples, plus faciles à comprendre et à lire.
  • La plupart des infrastructures Web et d'applications, ainsi que leurs "ORM" et leurs "enregistrements actifs", génèrent des requêtes avec sous-requêtes, car avec sous-requêtes, il est plus facile de séparer les responsabilités, de gérer le code, etc.
  • Sous-requêtes sont acceptables pour les sites Web plus petits, mais pour les sites Web et les applications plus grands, vous devrez souvent réécrire les requêtes générées en rejoindre requêtes, en particulier si une requête utilise plusieurs sous-requêtes dans la requête.

Certaines personnes disent "certains SGBDR peuvent réécrire un sous-requête en un {join) ou un joindre en un sous-requête quand il pense qu'on est plus rapide que l'autre. ", mais cette instruction s'applique aux cas simples, sûrement pas aux requêtes compliquées avec sous-requêtes qui posent effectivement des problèmes de performances.

4
fico7489

La différence n'est visible que lorsque la deuxième table de jointure contient beaucoup plus de données que la table principale. J'ai eu une expérience comme ci-dessous ...

Nous avions une table d'utilisateurs de cent mille entrées et leurs données d'adhésion (amitié) environ 3 cent mille entrées. C'était une déclaration de jointure afin de prendre des amis et leurs données, mais avec beaucoup de retard. Mais cela fonctionnait bien là où il n'y avait qu'une petite quantité de données dans la table d'appartenance. Une fois que nous avons changé pour utiliser une sous-requête, cela a bien fonctionné.

Mais dans l'intervalle, les requêtes de jointure fonctionnent avec d'autres tables comportant moins d'entrées que la table principale.

Je pense donc que les instructions de jointure et de sous-requête fonctionnent bien et que cela dépend des données et de la situation.

3
jpk

De nos jours, beaucoup de dbs peuvent optimiser les sous-requêtes et les jointures. Ainsi, il vous suffit d’examiner votre requête à l’aide d’expliquer et de voir laquelle est la plus rapide. Si les performances ne sont pas très différentes, je préfère utiliser les sous-requêtes car elles sont simples et faciles à comprendre.

2
Eunwoo Song

Je viens de penser au même problème, mais j'utilise une sous-requête dans la partie FROM . J'ai besoin de connecter et d'interroger de grandes tables, la table "esclave" a 28 millions d'enregistrements mais le résultat est seulement 128 si gros résultats. ! J'utilise la fonction MAX () dessus.

J'utilise d'abord LEFT JOIN car je pense que c'est la bonne façon, le mysql peut être optimisé, etc. La seconde fois, juste pour les tests, je réécris pour sous-sélectionner contre JOIN.

Runtime LEFT JOIN: 1.12s Runtime SUB-SELECT: 0.06s

18 fois plus rapide la sous-sélection que la jointure! Juste dans le adv Chokito. La sous-sélection a l'air terrible mais le résultat ...

0
Karoly Szabo