web-dev-qa-db-fra.com

JOIN requêtes vs plusieurs requêtes

Les requêtes JOIN sont-elles plus rapides que plusieurs requêtes? (Vous exécutez votre requête principale, puis de nombreux autres SELECT en fonction des résultats de votre requête principale)

Je demande parce que les joindre compliquerait BEAUCOUP la conception de mon application

Si elles sont plus rapides, est-ce que quelqu'un peut approximer très approximativement de combien? Si c'est 1,5 fois je m'en fiche, mais si c'est 10 fois je suppose que oui.

156
Thomas Bonini

C'est trop vague pour vous donner une réponse pertinente à votre cas. Cela dépend de beaucoup de choses. Jeff Atwood (fondateur de ce site) en fait a écrit à ce sujet . Pour la plupart, cependant, si vous avez les bons index et que vous faites correctement vos JOIN, il sera généralement plus rapide de faire un voyage que plusieurs.

71
Paolo Bergantino

Pour les jointures internes, une seule requête a du sens, car vous n'obtenez que les lignes correspondantes. Pour les jointures à gauche, les requêtes multiples sont bien meilleures… regardez le test de performance suivant que j'ai réalisé:

  1. Requête unique avec 5 jointures

    requête: 8.074508 secondes

    taille du résultat: 2268000

  2. 5 requêtes consécutives

    temps d'interrogation combiné: ,00262 secondes

    taille du résultat: 165 (6 + 50 + 7 + 12 + 90)

.

Notez que nous obtenons les mêmes résultats dans les deux cas (6 x 50 x 7 x 12 x 90 = 2268000)

les jointures à gauche utilisent de manière exponentielle plus de mémoire avec des données redondantes.

La limite de mémoire peut ne pas être aussi grave si vous ne faites qu'une jointure de deux tables, mais généralement de trois ou plus et que cela vaut des requêtes différentes.

En passant, mon serveur MySQL est juste à côté de mon serveur d'applications ... le temps de connexion est donc négligeable. Si votre temps de connexion est dans les secondes, alors peut-être il y a un avantage

Franc

86
Frank Forte

En fait, je suis arrivé à cette question en cherchant moi-même une réponse, et après avoir lu les réponses données, je ne peux que convenir que le meilleur moyen de comparer les performances des requêtes de base de données est d'obtenir des nombres réels car il y a trop de variables à prendre en compte. MAIS, je pense aussi que comparer les nombres entre eux ne donne pas de bons résultats dans presque tous les cas. Ce que je veux dire, c'est que les chiffres doivent toujours être comparés à un nombre acceptable et ne doivent absolument pas être comparés les uns aux autres.

Je peux comprendre que si une méthode d'interrogation prend 0,02 seconde, l'autre 20 secondes, c'est une différence énorme. Mais que se passe-t-il si une façon d'interroger prend 0,0000000002 secondes et que l'autre prend 0,0000002 secondes? Dans les deux cas, un moyen est 1000 fois plus rapide que l’autre, mais est-il vraiment toujours "énorme" dans le second cas?

En conclusion, comme je le vois personnellement: s’il fonctionne bien, optez pour la solution facile.

20
Valentin Flachsel

Faites un test rapide en sélectionnant une ligne dans une table de 50 000 lignes et en joignant une ligne dans une table de 100 000 lignes. Fondamentalement ressemblait à:

$id = mt_Rand(1, 50000);
$row = $db->fetchOne("SELECT * FROM table1 WHERE id = " . $id);
$row = $db->fetchOne("SELECT * FROM table2 WHERE other_id = " . $row['other_id']);

contre

$id = mt_Rand(1, 50000);
$db->fetchOne("SELECT table1.*, table2.*
    FROM table1
    LEFT JOIN table1.other_id = table2.other_id
    WHERE table1.id = " . $id);

La méthode de sélection deux prenait 3,7 secondes pour 50 000 lectures, tandis que la commande JOIN prenait 2,0 secondes sur mon ordinateur lent à la maison. INNER JOIN et LEFT JOIN n'ont pas fait de différence. La récupération de plusieurs lignes (par exemple, en utilisant IN SET) a donné des résultats similaires.

13
levans

Construisez à la fois des requêtes et des jointures distinctes, puis chronométrez chacune d'elles - rien n'est plus utile que des nombres réels.

Encore mieux - ajoutez "EXPLAIN" au début de chaque requête. Cela vous indiquera le nombre de sous-requêtes utilisées par MySQL pour répondre à votre demande de données et le nombre de lignes analysées pour chaque requête.

8
DreadPirateShawn

En fonction de la complexité de la base de données par rapport à la complexité du développeur, il peut être plus simple d'effectuer de nombreux appels SELECT.

Essayez d’exécuter des statistiques de base de données à la fois sur JOIN et sur les multiples SELECTS. Voyez si, dans votre environnement, JOIN est plus rapide/plus lent que SELECT.

Là encore, si le changer en JOIN signifierait un jour/semaine/mois de travail de développement supplémentaire, je resterais avec plusieurs SELECTs.

À votre santé,

BLT

7
glasnt

La vraie question est la suivante: Ces enregistrements ont-ils une relation un à un ou une relation ? relation -à-plusieurs ?

Réponse de TLDR:

S'il s'agit d'un one-to-one, utilisez une instruction JOIN.

S'il s'agit d'un à plusieurs, utilisez une (ou plusieurs) instruction SELECT avec une optimisation du code côté serveur.

Pourquoi et comment utiliser SELECT pour l'optimisation

SELECT 'ing (avec plusieurs requêtes au lieu de jointures) sur un grand groupe d'enregistrements basé sur une relation un à plusieurs produit une efficacité optimale, car JOIN' ing a un problème de fuite de mémoire exponentielle . Saisissez toutes les données, puis utilisez un langage de script côté serveur pour résoudre ce problème:

SELECT * FROM Address WHERE Personid IN(1,2,3);

Résultats:

Address.id : 1            // First person and their address
Address.Personid : 1
Address.City : "Boston"

Address.id : 2            // First person's second address
Address.Personid : 1
Address.City : "New York"

Address.id : 3            // Second person's address
Address.Personid : 2
Address.City : "Barcelona"

Ici, je reçois tous les enregistrements, dans une déclaration choisie. C'est mieux que JOIN, qui consiste à obtenir un petit groupe de ces enregistrements, un à la fois, en tant que sous-composant d'une autre requête. Ensuite, je l’analyse avec un code côté serveur qui ressemble à quelque chose comme ...

<?php
    foreach($addresses as $address) {
         $persons[$address['Personid']]->Address[] = $address;
    }
?>

Quand ne pas utiliser JOIN pour l'optimisation

JOIN 'un grand groupe d'enregistrements basé sur une relation un à un avec un seul enregistrement produit une efficacité optimale par rapport à plusieurs instructions SELECT, l'une après l'autre, qui obtiennent simplement le résultat souhaité. prochain type d'enregistrement.

Mais JOIN est inefficace pour obtenir des enregistrements avec une relation un à plusieurs.

Exemple: La base de données Blogs contient 3 tables d’intérêt, Blogpost, Tag et Comment.

SELECT * from BlogPost
LEFT JOIN Tag ON Tag.BlogPostid = BlogPost.id
LEFT JOIN Comment ON Comment.BlogPostid = BlogPost.id;

S'il y a 1 article de blog, 2 tags et 2 commentaires, vous obtiendrez des résultats tels que:

Row1: tag1, comment1,
Row2: tag1, comment2,
Row3: tag2, comment1,
Row4: tag2, comment2,

Notez comment chaque enregistrement est dupliqué. D'accord, donc, 2 commentaires et 2 balises représentent 4 lignes. Et si nous avons 4 commentaires et 4 tags? Vous n'obtenez pas 8 lignes - vous obtenez 16 lignes:

Row1: tag1, comment1,
Row2: tag1, comment2,
Row3: tag1, comment3,
Row4: tag1, comment4,
Row5: tag2, comment1,
Row6: tag2, comment2,
Row7: tag2, comment3,
Row8: tag2, comment4,
Row9: tag3, comment1,
Row10: tag3, comment2,
Row11: tag3, comment3,
Row12: tag3, comment4,
Row13: tag4, comment1,
Row14: tag4, comment2,
Row15: tag4, comment3,
Row16: tag4, comment4,

Ajoutez plus de tables, plus d'enregistrements, etc., et le problème se multipliera rapidement en centaines de lignes remplies de la plupart du temps de données redondantes.

Qu'est-ce que ces doublons vous coûtent? Mémoire (sur le serveur SQL et le code qui tente de supprimer les doublons) et ressources réseau (entre le serveur SQL et votre serveur de code).

Source: https://dev.mysql.com/doc/refman/8.0/en/nested-join-optimization.html ; https://dev.mysql.com/doc/workbench/en/wb-relationship-tools.html

6
HoldOffHunger

Cette question est ancienne, mais il manque quelques points de repère. J'ai comparé JOIN avec ses 2 concurrents:

  • N + 1 requêtes
  • 2 requêtes, la seconde utilisant un WHERE IN(...) ou équivalent

Le résultat est clair: sur MySQL, JOIN est beaucoup plus rapide . N + 1 requêtes peuvent considérablement réduire les performances d'une application:

JOIN vs WHERE IN vs N+1

Autrement dit, à moins que vous ne sélectionniez un grand nombre d'enregistrements qui indiquent un très petit nombre d'enregistrements étrangers distincts. Voici un repère pour le cas extrême:

JOIN vs N+1 - all records pointing to the same foreign record

Il est très peu probable que cela se produise dans une application typique, à moins que vous ne rejoigniez une relation "plusieurs à plusieurs", auquel cas la clé étrangère est sur l'autre table et que vous dupliquez plusieurs fois les données de la table principale.

À emporter:

  • Pour les relations * à un, utilisez toujours JOIN
  • Pour les relations * -à-plusieurs, une seconde requête pourrait être plus rapide

Voir mon article sur Medium pour plus d'informations.

5
Benjamin

D'après mon expérience, il est généralement plus rapide d'exécuter plusieurs requêtes, notamment lors de la récupération de grands ensembles de données.

Lors de l'interaction avec la base de données à partir d'une autre application, telle que PHP, l'argument d'un voyage sur le serveur sur plusieurs est utilisé.

Il existe d'autres moyens de limiter le nombre de trajets effectués sur le serveur tout en continuant à exécuter plusieurs requêtes souvent non seulement plus rapides, mais facilitant également la lecture de l'application, par exemple mysqli_multi_query.

Je ne suis pas un novice en matière de SQL. Je pense que les développeurs, en particulier les développeurs juniors, ont tendance à passer beaucoup de temps à écrire des jointures très intelligentes, car elles ont simple.

Le dernier paragraphe était une opinion personnelle, mais j'espère que cela aidera. Je suis d'accord avec les autres cependant qui disent que vous devriez comparer. Aucune approche n'est une solution miracle.

5
A Boy Named Su

Sera-ce plus rapide en termes de débit? Probablement. Mais il peut aussi potentiellement verrouiller plusieurs objets de base de données à la fois (en fonction de votre base de données et de votre schéma) et ainsi réduire la simultanéité. Selon mon expérience, les gens sont souvent induits en erreur par l'argument "nombre réduit d'allers-retours de base de données" alors qu'en réalité, sur la plupart des systèmes OLTP où la base de données se trouve sur le même réseau local, le véritable goulot d'étranglement est rarement le réseau.

3
Ramon

Voici un lien avec 100 requêtes utiles, celles-ci sont testées dans une base de données Oracle, mais rappelez-vous que SQL est une norme, ce qui diffère entre Oracle, MS SQL Server, MySQL et d'autres bases de données sont le dialecte SQL:

http://javaforlearn.com/100-sql-queries-learn/

2
S. Mayol

Il y a plusieurs facteurs qui font qu'il n'y a pas de réponse binaire. La question de savoir ce qui est le mieux pour les performances dépend de votre environnement. En passant, si votre single select avec un identifiant n’est pas inférieur à une seconde, il est possible que votre configuration soit erronée.

La vraie question à poser est de savoir comment vous souhaitez accéder aux données. Une seule sélection prend en charge la liaison tardive. Par exemple, si vous souhaitez uniquement des informations sur les employés, vous pouvez effectuer une sélection dans la table Employés. Les relations de clé étrangère peuvent être utilisées pour extraire des ressources connexes ultérieurement et au besoin. Les sélections auront déjà une clé à pointer, elles doivent donc être extrêmement rapides et il vous suffit de récupérer ce dont vous avez besoin. La latence du réseau doit toujours être prise en compte.

Les jointures vont récupérer toutes les données à la fois. Si vous générez un rapport ou remplissez une grille, cela peut être exactement ce que vous voulez. Les jointures compilées et optimisées vont tout simplement être plus rapides que les sélections uniques dans ce scénario. N'oubliez pas que les jointures ad-hoc peuvent ne pas être aussi rapides. Vous devez les compiler (dans un processus stocké). La réponse rapide dépend du plan d'exécution, qui détaille exactement les étapes que le SGBD doit suivre pour extraire les données.

1
dr.lockett

La question de savoir si vous devez utiliser une jointure est avant tout de savoir si une jointure a un sens . Ce n’est qu’à ce stade que les performances peuvent même être prises en compte, étant donné que presque tous les autres cas entraînent des performances considérablement inférieures .

Les différences de performances dépendent en grande partie de la relation entre les informations que vous interrogez. Les jointures fonctionnent, et elles sont rapides lorsque les données sont liées et que vous indexez les éléments correctement, mais elles entraînent souvent une certaine redondance et parfois plus de résultats que nécessaire. Et si vos ensembles de données ne sont pas directement liés, si vous les collez dans une seule requête, vous obtiendrez ce que l'on appelle un produit cartésien (en gros, toutes les combinaisons possibles de lignes), ce qui n'est presque jamais ce que vous voulez.

Ceci est souvent causé par des relations plusieurs à un à plusieurs. Par exemple, réponse de HoldOffHunger a mentionné une requête unique pour les publications, les balises et les commentaires. Les commentaires sont liés à un message, de même que les balises ... mais les balises ne sont pas liées aux commentaires.

+------------+     +---------+     +---------+
|  comment   |     |   post  |     |  tag    |
|------------|*   1|---------|1   *|---------|
| post_id    |-----| post_id |-----| post_id |
| comment_id |     | ...     |     | tag_id  |
| user_id    |     |         |     | ...     |
| ...        |     |         |     | ...     |
+------------+     +---------+     +---------+

Dans ce cas, il est nettement préférable qu'il s'agisse d'au moins deux requêtes distinctes. Si vous essayez de joindre des balises et des commentaires, car il n'y a pas de relation directe entre les deux, vous obtenez toutes les combinaisons possibles de balises et de commentaires. many * many == manymany. En dehors de cela, comme les publications et les balises ne sont pas liées, vous pouvez effectuer ces deux requêtes en parallèle, ce qui pourrait vous apporter un gain potentiel.

Prenons un autre scénario: vous souhaitez que les commentaires soient attachés à un message et les informations de contact des commentateurs.

 +----------+     +------------+     +---------+
 |   user   |     |  comment   |     |   post  |
 |----------|1   *|------------|*   1|---------|
 | user_id  |-----| post_id    |-----| post_id |
 | username |     | user_id    |     | ...     |
 | ...      |     | ...        |     +---------+
 +----------+     +------------+

C'est là que vous devriez envisager une jointure. En plus d'être une requête beaucoup plus naturelle, la plupart des systèmes de base de données (y compris MySQL) ont beaucoup de gens intelligents qui travaillent d'arrache-pied à l'optimisation des requêtes. Pour des requêtes distinctes, puisque chaque requête dépend des résultats de la précédente, les requêtes ne peuvent pas être effectuées en parallèle et le temps total devient non seulement le temps d'exécution réel des requêtes, mais également le temps passé à extraire les résultats, à tamiser à travers eux pour les identifiants de la requête suivante, reliant les lignes entre elles, etc.

1
cHao

Oui, une requête utilisant JOINS serait plus rapide. Bien que, sans connaître les relations entre les tables que vous interrogez, la taille de votre ensemble de données ou l'emplacement des clés primaires, il est presque impossible de dire à quel point il est plus rapide.

Pourquoi ne pas tester les deux scénarios, alors vous saurez à coup sûr ...

0
Mathew