web-dev-qa-db-fra.com

Les requêtes individuelles sont-elles plus rapides que les jointures?

Question conceptuelle: les requêtes individuelles sont-elles plus rapides que les jointures, ou: Dois-je essayer de compresser toutes les informations que je veux du côté client en une SELECT ou utilisez-en autant qu'il vous semble commode?

TL; DR : Si mon la requête jointe prend plus de temps que l'exécution de requêtes individuelles, est-ce ma faute ou est-ce à être attendu?

Tout d'abord, je ne suis pas très averti des bases de données, il se peut donc que ce soit juste moi, mais j'ai remarqué que lorsque je dois obtenir des informations de plusieurs tables, il est "souvent" plus rapide d'obtenir ces informations via plusieurs requêtes sur des tables individuelles (peut-être contenant une simple jointure interne) et corrigez les données ensemble côté client pour essayer d'écrire une requête jointe (complexe) où je peux obtenir toutes les données en une seule requête.

J'ai essayé de rassembler un exemple extrêmement simple:

SQL Fiddle

Configuration du schéma :

CREATE TABLE MASTER 
( ID INT NOT NULL
, NAME VARCHAR2(42 CHAR) NOT NULL
, CONSTRAINT PK_MASTER PRIMARY KEY (ID)
);

CREATE TABLE DATA
( ID INT NOT NULL
, MASTER_ID INT NOT NULL
, VALUE NUMBER
, CONSTRAINT PK_DATA PRIMARY KEY (ID)
, CONSTRAINT FK_DATA_MASTER FOREIGN KEY (MASTER_ID) REFERENCES MASTER (ID)
);

INSERT INTO MASTER values (1, 'One');
INSERT INTO MASTER values (2, 'Two');
INSERT INTO MASTER values (3, 'Three');

CREATE SEQUENCE SEQ_DATA_ID;

INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.5);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.7);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 2, 2.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.14);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.7);

Requête A :

select NAME from MASTER
where ID = 1

Résultats:

| NAME |
--------
|  One |

Requête B :

select ID, VALUE from DATA
where MASTER_ID = 1

Résultats:

| ID | VALUE |
--------------
|  1 |   1.3 |
|  2 |   1.5 |
|  3 |   1.7 |

Requête C :

select M.NAME, D.ID, D.VALUE 
from MASTER M INNER JOIN DATA D ON M.ID=D.MASTER_ID
where M.ID = 1

Résultats:

| NAME | ID | VALUE |
---------------------
|  One |  1 |   1.3 |
|  One |  2 |   1.5 |
|  One |  3 |   1.7 |

Bien sûr, je n'ai mesuré aucune performance avec ceux-ci, mais on peut observer:

  • La requête A + B renvoie la même quantité d'informations utilisables que la requête C.
  • A + B doit retourner 1 + 2x3 == 7 "Cellules de données" au client
  • C doit retourner 3x3 == 9 "Cellules de données" au client, car avec la jointure, j'inclus naturellement une certaine redondance dans le jeu de résultats.

Généralisation à partir de cela (aussi farfelue qu'elle soit):

Une requête jointe toujours doit renvoyer plus de données que les requêtes individuelles qui reçoivent la même quantité d'informations. Puisque la base de données doit brouiller les données, pour les grands ensembles de données on peut supposer que la base de données doit faire plus de travail sur une seule requête jointe que sur les requêtes individuelles, car (au moins) elle doit renvoyer plus de données au client.

En résulterait-il que, lorsque j'observe que le fractionnement d'une requête côté client en plusieurs requêtes donne de meilleures performances, c'est juste le chemin à parcourir, ou cela signifie-t-il plutôt que j'ai foiré la requête jointe?

46
Martin

Les requêtes individuelles sont-elles plus rapides que les jointures, ou: Dois-je essayer de compresser toutes les informations que je veux du côté client en une seule instruction SELECT ou simplement en utiliser autant que cela semble pratique?

Dans tout scénario de performance, vous devez tester et mesurer les solutions pour voir laquelle est la plus rapide .

Cela dit, il est presque toujours le cas qu'un jeu de résultats joint à partir d'une base de données correctement réglée sera plus rapide et évoluera mieux que de renvoyer les lignes source au client, puis de les y rejoindre. En particulier, si les jeux d'entrée sont volumineux et que le jeu de résultats est petit - pensez à la requête suivante dans le contexte des deux stratégies: réunissez deux tables de 5 Go chacune, avec un jeu de résultats de 100 lignes. C'est un extrême, mais vous voyez mon point.

J'ai remarqué que lorsque je dois obtenir des informations de plusieurs tables, il est "souvent" plus rapide d'obtenir ces informations via plusieurs requêtes sur des tables individuelles (contenant peut-être une simple jointure interne) et de corriger les données ensemble côté client à essayer. pour écrire une requête jointe (complexe) où je peux obtenir toutes les données en une seule requête.

Il est très probable que le schéma ou les index de la base de données pourraient être améliorés pour mieux répondre aux requêtes que vous lui lancez.

Une requête jointe doit toujours renvoyer plus de données que les requêtes individuelles qui reçoivent la même quantité d'informations.

Ce n'est généralement pas le cas. La plupart du temps, même si les ensembles d'entrées sont volumineux, l'ensemble de résultats sera beaucoup plus petit que la somme des entrées.

Selon l'application, des jeux de résultats de requête très volumineux renvoyés au client sont un signal d'alarme immédiat: que fait le client avec un si grand ensemble de données qui ne peut pas être fait plus près de la base de données? L'affichage de 1 000 000 de lignes à un utilisateur est pour le moins hautement suspect. La bande passante réseau est également une ressource limitée.

Étant donné que la base de données doit brouiller les données, pour les grands ensembles de données, on peut supposer que la base de données doit faire plus de travail sur une seule requête jointe que sur les requêtes individuelles, car (au moins) elle doit renvoyer plus de données au client.

Pas nécessairement. Si les données sont indexées correctement, l'opération de jointure est plus susceptible d'être effectuée plus efficacement dans la base de données sans avoir besoin d'analyser une grande quantité de données. De plus, les moteurs de bases de données relationnelles sont spécialement optimisés à un faible niveau pour se joindre ; les piles de clients ne le sont pas.

En résulterait-il que, lorsque j'observe que le fractionnement d'une requête côté client en plusieurs requêtes donne de meilleures performances, c'est juste le chemin à parcourir, ou cela signifie-t-il plutôt que j'ai foiré la requête jointe?

Puisque vous avez dit que vous n'aviez aucune expérience en matière de bases de données, je suggère d'en savoir plus sur la conception de bases de données et le réglage des performances. Je suis sûr que c'est là que réside le problème. Des requêtes SQL écrites de manière inefficace sont également possibles, mais avec un schéma simple qui est moins susceptible de poser problème.

Maintenant, cela ne veut pas dire qu'il n'y a pas d'autres façons d'améliorer les performances. Il existe des scénarios dans lesquels vous pouvez choisir d'analyser un ensemble de données de taille moyenne à grande et de le renvoyer au client si l'intention est d'utiliser une sorte de mécanisme de mise en cache. La mise en cache peut être excellente, mais elle introduit de la complexité dans votre conception. La mise en cache peut même ne pas être appropriée pour votre application.

Une chose qui n'a été mentionnée nulle part est le maintien de la cohérence des données renvoyées par la base de données. Si des requêtes distinctes sont utilisées, il est plus probable (en raison de nombreux facteurs) que des données incohérentes soient renvoyées, sauf si une forme d'isolement de capture instantanée est utilisée pour chaque ensemble de requêtes.

46
Jon Seigel

Bien sûr, je n'ai mesuré aucune performance avec ces

Vous avez rassemblé un bon exemple de code. Avez-vous regardé le timing dans SQL Fiddle? Même de brefs tests de performances non scientifiques montreront que la requête trois dans votre démonstration prend environ le même temps pour s'exécuter que la requête un ou deux séparément. Un et deux combinés prennent environ deux fois plus longtemps que trois, c'est-à-dire avant toute jointure côté client est effectuée.

À mesure que vous augmentez les données, la vitesse des requêtes un et deux divergent, mais la jointure de la base de données est toujours plus rapide.

Vous devez également considérer ce qui se passerait si la jointure interne supprimait les données.

6
Leigh Riffel

L'optimiseur de requêtes doit également être pris en compte. Son rôle est de prendre votre SQL déclaratif et de le traduire en étapes procédurales. Pour trouver la combinaison la plus efficace d'étapes procédurales, il examinera également les combinaisons d'utilisation des index, les tris, la mise en cache des jeux de résultats intermédiaires et toutes sortes d'autres choses. Le nombre de permutations peut devenir extrêmement important même avec ce qui ressemble à des requêtes assez simples.

Une grande partie du calcul effectué pour trouver le meilleur plan est motivée par la distribution des données dans les tableaux. Ces distributions sont échantillonnées et stockées en tant qu'objets statistiques. S'ils sont erronés, ils conduisent l'optimiseur à faire de mauvais choix. De mauvais choix au début du plan conduisent à des choix encore plus pauvres plus tard dans un effet Snowball.

Il n'est pas inconnu qu'une requête de taille moyenne renvoyant de modestes quantités de données prenne quelques minutes à s'exécuter. Une indexation correcte et de bonnes statistiques le réduisent ensuite en millisecondes.

2
Michael Green