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:
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
| NAME |
--------
| One |
Requête B :
select ID, VALUE from DATA
where MASTER_ID = 1
| 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
| 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?
Réponses:
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.
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.
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.
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.
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.
la source
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.
la source
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.
la source
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.
la source