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

44

Question conceptuelle: les requêtes individuelles sont-elles plus rapides que les jointures, ou: dois-je essayer de compresser toutes les informations souhaitées du côté client dans une seule instruction SELECT ou tout simplement d’en utiliser autant que cela semble utile?

TL; DR : Si ma requête jointe prend plus de temps que d'exécuter des requêtes individuelles, est-ce ma faute ou est-ce à prévoir?

Tout d’abord, je ne suis pas très familiarisé avec les bases de données, c’est donc peut-être juste moi, mais j’ai remarqué que lorsque je dois obtenir des informations à partir 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 du côté client afin d’essayer d’écrire une requête jointe (complexe) dans laquelle je peux obtenir toutes les données d’une seule requête.

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

Violon SQL

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 celles-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 renvoyer 1 + 2x3 == 7 "cellules de données" au client.
  • C doit renvoyer 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éraliser à partir de cela (aussi poussé qu'il soit):

Une requête jointe doit toujours renvoyer plus de données que les requêtes individuelles recevant la même quantité d'informations. Étant donné que la base de données doit regrouper les données, on peut supposer que pour les grands ensembles de données, elle doit travailler davantage sur une requête jointe que sur des requêtes individuelles, car (au moins) elle doit renvoyer plus de données au client.

Cela signifierait-il que, lorsque j'observe que le fractionnement d'une requête côté client en plusieurs requêtes génère de meilleures performances, c'est tout simplement la voie à suivre, ou est-ce que cela voudrait dire que j'ai foiré la requête jointe?

Martin
la source
Les commentaires ne sont pas pour une discussion prolongée; cette conversation a été déplacée pour discuter .
Jack Douglas
1
J'ai fait un benchmark et posté les résultats dans un article sur Medium . J'aurais ajouté une réponse ici, mais je l'ai déjà fait sur une autre question , et poster la même réponse à plusieurs questions est mal vu .
Benjamin le

Réponses:

45

Les requêtes individuelles sont-elles plus rapides que les jointures ou: dois-je essayer de compresser toutes les informations souhaitées côté client dans une seule instruction SELECT ou tout simplement d’en utiliser autant qu’il semble utile?

Quel que soit le scénario de performance, vous devez tester et mesurer les solutions pour déterminer laquelle est la plus rapide .

Cela dit, il est presque toujours possible qu'un ensemble de résultats réunis à partir d'une base de données correctement paramétrée soit plus rapide et évolue mieux que de renvoyer les lignes source au client, puis de les y joindre. En particulier, si les jeux d'entrée sont volumineux et si le jeu de résultats est petit, réfléchissez à la requête suivante dans le contexte des deux stratégies: joignez 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 à partir 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 patcher les données ensemble côté client. écrire une requête jointe (complexe) où je peux obtenir toutes les données en une requête.

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

Une requête jointe doit toujours renvoyer plus de données que les requêtes individuelles recevant 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 grands, l'ensemble de résultats sera beaucoup plus petit que la somme des entrées.

En fonction de l'application, les très grands ensembles de résultats de requête renvoyés au client constituent un indicateur rouge immédiat: que fait le client avec un si grand ensemble de données qui ne peut pas être rapproché de la base de données? Afficher un million de lignes à un utilisateur est pour le moins suspect. La bande passante du réseau est également une ressource finie.

Étant donné que la base de données doit rassembler les données, on peut supposer que pour les grands ensembles de données, elle doit travailler davantage sur une requête jointe que sur des 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 sera probablement plus efficace dans la base de données sans avoir à analyser une grande quantité de données. De plus, les moteurs de bases de données relationnelles sont spécialement optimisés à un niveau bas pour la jonction ; les piles de clients ne le sont pas.

Cela signifierait-il que, lorsque j'observe que le fractionnement d'une requête côté client en plusieurs requêtes génère de meilleures performances, c'est tout simplement la voie à suivre, ou est-ce que cela voudrait dire que j'ai foiré la requête jointe?

Puisque vous avez dit que vous n’êtes pas expérimenté en matière de bases de données, je suggérerais d’en apprendre davantage 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 mal écrites sont également possibles, mais avec un schéma simple, moins susceptible de poser problème.

Cela ne veut pas dire qu'il n'y a pas d'autres moyens d'améliorer les performances. Il existe des scénarios dans lesquels vous pouvez choisir d’analyser un ensemble de données de moyenne à grande taille 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 formidable, 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 jamais été mentionnée est de maintenir 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'isolation de capture instantanée est utilisée pour chaque ensemble de requêtes.

Jon Seigel
la source
+1 pour la bande passante réseau est également une ressource finie.
Hari Harker le
OP dit que les ensembles de résultats de données jointes sont toujours plus grands. > Une requête jointe doit toujours renvoyer plus de données que les requêtes individuelles. Je pense que cela est objectivement vrai (pour> =), par exemple, les ensembles de résultats diffèrent en taille, donc plus de données sur le réseau. Avez-vous un exemple où ce n'est pas vrai? Si je m'inscris à Auteurs -> Articles et auteurs a un champ appelé "biographie" qui est un champ JSON de 1 Mo, pour un auteur de 100 messages, je transmettrai sur le fil 100 Mo contre 1 Mo. Est-ce faux?
hytromo
6

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

Vous avez mis en place 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 de votre démonstration prend environ le même temps à s'exécuter que la requête un ou deux séparément. La combinaison des opérations un et deux prend environ deux fois plus de temps que trois, c'est-à-dire avant toute jointure côté client.

Au fur et à 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 reste plus rapide.

Vous devez également envisager ce qui se produirait si la jointure interne éliminait les données.

Leigh Riffel
la source
2

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 de procédure, elle examinera également des combinaisons d’utilisation d’index, de tris, de mise en cache d’ensembles de résultats intermédiaires et de nombreuses autres choses. Le nombre de permutations peut devenir extrêmement important, même avec ce qui ressemble à des requêtes assez simples.

La plupart des calculs effectués pour trouver le meilleur plan sont déterminés par la distribution des données dans les tableaux. Ces distributions sont échantillonnées et stockées sous forme d'objets statistiques. S'ils sont incorrects, ils conduisent l'optimiseur à faire de mauvais choix. Les mauvais choix pris tôt dans le plan ont conduit à des choix encore plus pauvres plus tard, avec un effet boule de neige.

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

Michael Green
la source
-3

Plusieurs requêtes sont la voie à suivre. Si vous gérez des scénarios simples comme celui-ci, les coûts supplémentaires liés à l'optimiseur de requêtes sont un facteur. Avec plus de données, l'inefficacité réseau de la jointure (lignes redondantes) intervient. L'efficacité est d'autant plus grande que les données sont plus nombreuses.

À la fin, de nombreux développeurs voient ce que vous vivez. Les administrateurs de bases de données disent toujours "non, faites une jointure", mais la réalité est la suivante: il est plus rapide de faire plusieurs sélections simples dans ce cas.

TomTom
la source
5
Il n'y a pas "d'inefficacité réseau" dans une jointure - tout se passe sur le serveur de base de données, il n'y a donc pas de réseau impliqué (sauf si vous vous connectez via un lien de base de données!)
Chris Saxon
2
Vous voudrez peut-être déterminer si la couche réseau est compressée ou non. Le SQL * Net d’Oracle le fait en ce sens que les valeurs répétées dans la même colonne sont compressées efficacement.
David Aldridge
3
@TomTom vous pouvez avoir un point ou non (comme le souligne David Aldridge, la compression compte), mais votre formulation est confuse. "inefficacité du réseau de la jointure" ? Vraiment, corrigez cela afin que ce que vous entendiez par écrit soit évident.
Ypercubeᵀᴹ
@ChrisSaxon bien sur, image vous avez des tables pour un rapport "titre-> base-> tables-lignes" et vous avez besoin de toutes les lignes pour pouvoir joindre ces 3 tables. Chaque table a de longs varchars, donc ce qui se passe correspond à chaque ligne où vous répétez ces longs varchars. La couche d'application doit allouer de la mémoire pour toutes ces chaînes, puis les regrouper pour votre modèle. Donc, je pense que c'est ce qu'il veut dire, il y a plus de données envoyées
MIKE
@MIKE cela dépend des expressions que vous sélectionnez, pas de la jointure. Et il peut y avoir une compression réseau. Dans Oracle Database SQL * Net supprime les doublons répétés nicetheory.io/2018/01/11/…
Chris Saxon